diff --git a/Cable.xcodeproj/project.pbxproj b/Cable.xcodeproj/project.pbxproj index 5d9a343..a1ee9e3 100644 --- a/Cable.xcodeproj/project.pbxproj +++ b/Cable.xcodeproj/project.pbxproj @@ -6,13 +6,6 @@ objectVersion = 77; objects = { -/* Begin PBXBuildFile section */ - 0DBC1CAB8BE5C690AE39630C /* Pods_Cable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B59C6617C5C88811F972C70 /* Pods_Cable.framework */; }; - 156FA26BC2A070D3E79DBC53 /* Pods_Cable_CableUITestsScreenshot.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838FF4DAE13C48C1BCC63760 /* Pods_Cable_CableUITestsScreenshot.framework */; }; - 4472B945421CAB58A81AAF03 /* Pods_Cable_CableUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCB07623E3B49D249C01C67E /* Pods_Cable_CableUITests.framework */; }; - 85A2E22A9DF253A619C833B2 /* Pods_CableTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 251C4DF01338D1FECB418EE7 /* Pods_CableTests.framework */; }; -/* End PBXBuildFile section */ - /* Begin PBXContainerItemProxy section */ 3E37F65B2E93FB6F00836187 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -38,22 +31,10 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0A51CE1631634DF868118C1B /* Pods-CableTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CableTests.debug.xcconfig"; path = "Target Support Files/Pods-CableTests/Pods-CableTests.debug.xcconfig"; sourceTree = ""; }; - 10D59C9B7039F7390CB71DAA /* Pods-CableTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CableTests.release.xcconfig"; path = "Target Support Files/Pods-CableTests/Pods-CableTests.release.xcconfig"; sourceTree = ""; }; - 251C4DF01338D1FECB418EE7 /* Pods_CableTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CableTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2B59C6617C5C88811F972C70 /* Pods_Cable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Cable.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 340C908BC5784DC053266DDB /* Pods-Cable-CableUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Cable-CableUITests.debug.xcconfig"; path = "Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.debug.xcconfig"; sourceTree = ""; }; 3E37F6552E93FB6F00836187 /* CableUITestsScreenshot.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CableUITestsScreenshot.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3E5C0BCC2E72C0FD00247EC8 /* Cable.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Cable.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3E5C0BDD2E72C0FE00247EC8 /* CableTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CableTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3E5C0BE72E72C0FE00247EC8 /* CableUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CableUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 4017B33DF440FA2BC612E06E /* Pods-Cable-CableUITestsScreenshot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Cable-CableUITestsScreenshot.release.xcconfig"; path = "Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.release.xcconfig"; sourceTree = ""; }; - 838FF4DAE13C48C1BCC63760 /* Pods_Cable_CableUITestsScreenshot.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Cable_CableUITestsScreenshot.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 83D6CB62ED3959EC1EC8027D /* Pods-Cable-CableUITestsScreenshot.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Cable-CableUITestsScreenshot.debug.xcconfig"; path = "Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.debug.xcconfig"; sourceTree = ""; }; - B5E79A38FD11ED9D9A21BB7E /* Pods-Cable.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Cable.debug.xcconfig"; path = "Target Support Files/Pods-Cable/Pods-Cable.debug.xcconfig"; sourceTree = ""; }; - BCB07623E3B49D249C01C67E /* Pods_Cable_CableUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Cable_CableUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BED2A9D04FDB84725E0725E9 /* Pods-Cable.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Cable.release.xcconfig"; path = "Target Support Files/Pods-Cable/Pods-Cable.release.xcconfig"; sourceTree = ""; }; - F4D8F0C9760202BC765B4260 /* Pods-Cable-CableUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Cable-CableUITests.release.xcconfig"; path = "Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -64,11 +45,21 @@ ); target = 3E5C0BCB2E72C0FD00247EC8 /* Cable */; }; + 3EB0C1772EBBAF8F007BAFC4 /* Exceptions for "CableUITestsScreenshot" folder in "CableUITestsScreenshot" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + CableUITestsScreenshotLaunchTests.swift, + ); + target = 3E37F6542E93FB6F00836187 /* CableUITestsScreenshot */; + }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ 3E37F6562E93FB6F00836187 /* CableUITestsScreenshot */ = { isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 3EB0C1772EBBAF8F007BAFC4 /* Exceptions for "CableUITestsScreenshot" folder in "CableUITestsScreenshot" target */, + ); path = CableUITestsScreenshot; sourceTree = ""; }; @@ -97,7 +88,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 156FA26BC2A070D3E79DBC53 /* Pods_Cable_CableUITestsScreenshot.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -105,7 +95,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0DBC1CAB8BE5C690AE39630C /* Pods_Cable.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -113,7 +102,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 85A2E22A9DF253A619C833B2 /* Pods_CableTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -121,7 +109,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4472B945421CAB58A81AAF03 /* Pods_Cable_CableUITests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -137,7 +124,6 @@ 3E37F6562E93FB6F00836187 /* CableUITestsScreenshot */, 3E5C0BCD2E72C0FD00247EC8 /* Products */, 57738E9B07763CFA62681EEE /* Pods */, - 9D16D1FE8C8B34C13C51D389 /* Frameworks */, ); sourceTree = ""; }; @@ -155,29 +141,10 @@ 57738E9B07763CFA62681EEE /* Pods */ = { isa = PBXGroup; children = ( - B5E79A38FD11ED9D9A21BB7E /* Pods-Cable.debug.xcconfig */, - BED2A9D04FDB84725E0725E9 /* Pods-Cable.release.xcconfig */, - 340C908BC5784DC053266DDB /* Pods-Cable-CableUITests.debug.xcconfig */, - F4D8F0C9760202BC765B4260 /* Pods-Cable-CableUITests.release.xcconfig */, - 83D6CB62ED3959EC1EC8027D /* Pods-Cable-CableUITestsScreenshot.debug.xcconfig */, - 4017B33DF440FA2BC612E06E /* Pods-Cable-CableUITestsScreenshot.release.xcconfig */, - 0A51CE1631634DF868118C1B /* Pods-CableTests.debug.xcconfig */, - 10D59C9B7039F7390CB71DAA /* Pods-CableTests.release.xcconfig */, ); path = Pods; sourceTree = ""; }; - 9D16D1FE8C8B34C13C51D389 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 2B59C6617C5C88811F972C70 /* Pods_Cable.framework */, - BCB07623E3B49D249C01C67E /* Pods_Cable_CableUITests.framework */, - 838FF4DAE13C48C1BCC63760 /* Pods_Cable_CableUITestsScreenshot.framework */, - 251C4DF01338D1FECB418EE7 /* Pods_CableTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -185,11 +152,9 @@ isa = PBXNativeTarget; buildConfigurationList = 3E37F65D2E93FB6F00836187 /* Build configuration list for PBXNativeTarget "CableUITestsScreenshot" */; buildPhases = ( - ECF8C5947A59DAC9118AE4F4 /* [CP] Check Pods Manifest.lock */, 3E37F6512E93FB6F00836187 /* Sources */, 3E37F6522E93FB6F00836187 /* Frameworks */, 3E37F6532E93FB6F00836187 /* Resources */, - 611809BC8E1F9DF30E9C4629 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -208,11 +173,9 @@ isa = PBXNativeTarget; buildConfigurationList = 3E5C0BF02E72C0FE00247EC8 /* Build configuration list for PBXNativeTarget "Cable" */; buildPhases = ( - 3585B809C20C4D3B1FE82C78 /* [CP] Check Pods Manifest.lock */, 3E5C0BC82E72C0FD00247EC8 /* Sources */, 3E5C0BC92E72C0FD00247EC8 /* Frameworks */, 3E5C0BCA2E72C0FD00247EC8 /* Resources */, - E8C196B44C4F00DA4E300C55 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -230,7 +193,6 @@ isa = PBXNativeTarget; buildConfigurationList = 3E5C0BF52E72C0FE00247EC8 /* Build configuration list for PBXNativeTarget "CableTests" */; buildPhases = ( - 3D80694CE29BD68AE168E8DF /* [CP] Check Pods Manifest.lock */, 3E5C0BD92E72C0FE00247EC8 /* Sources */, 3E5C0BDA2E72C0FE00247EC8 /* Frameworks */, 3E5C0BDB2E72C0FE00247EC8 /* Resources */, @@ -252,11 +214,9 @@ isa = PBXNativeTarget; buildConfigurationList = 3E5C0BF82E72C0FE00247EC8 /* Build configuration list for PBXNativeTarget "CableUITests" */; buildPhases = ( - D1A689F595A65E0530AAACB0 /* [CP] Check Pods Manifest.lock */, 3E5C0BE32E72C0FE00247EC8 /* Sources */, 3E5C0BE42E72C0FE00247EC8 /* Frameworks */, 3E5C0BE52E72C0FE00247EC8 /* Resources */, - 3808009BC0D951592701EA88 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -355,160 +315,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 3585B809C20C4D3B1FE82C78 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Cable-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 3808009BC0D951592701EA88 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 3D80694CE29BD68AE168E8DF /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-CableTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 611809BC8E1F9DF30E9C4629 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - D1A689F595A65E0530AAACB0 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Cable-CableUITests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - E8C196B44C4F00DA4E300C55 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Cable/Pods-Cable-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Cable/Pods-Cable-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Cable/Pods-Cable-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - ECF8C5947A59DAC9118AE4F4 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Cable-CableUITestsScreenshot-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 3E37F6512E93FB6F00836187 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -561,7 +367,6 @@ /* Begin XCBuildConfiguration section */ 3E37F65E2E93FB6F00836187 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 83D6CB62ED3959EC1EC8027D /* Pods-Cable-CableUITestsScreenshot.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -583,7 +388,6 @@ }; 3E37F65F2E93FB6F00836187 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4017B33DF440FA2BC612E06E /* Pods-Cable-CableUITestsScreenshot.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -605,7 +409,6 @@ }; 3E5C0BF12E72C0FE00247EC8 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B5E79A38FD11ED9D9A21BB7E /* Pods-Cable.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -642,7 +445,6 @@ }; 3E5C0BF22E72C0FE00247EC8 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BED2A9D04FDB84725E0725E9 /* Pods-Cable.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -802,7 +604,6 @@ }; 3E5C0BF62E72C0FE00247EC8 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0A51CE1631634DF868118C1B /* Pods-CableTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -822,7 +623,6 @@ }; 3E5C0BF72E72C0FE00247EC8 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 10D59C9B7039F7390CB71DAA /* Pods-CableTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -842,7 +642,6 @@ }; 3E5C0BF92E72C0FE00247EC8 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 340C908BC5784DC053266DDB /* Pods-Cable-CableUITests.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -860,7 +659,6 @@ }; 3E5C0BFA2E72C0FE00247EC8 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F4D8F0C9760202BC765B4260 /* Pods-Cable-CableUITests.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; diff --git a/Cable.xcworkspace/contents.xcworkspacedata b/Cable.xcworkspace/contents.xcworkspacedata index 7552daa..49829ff 100644 --- a/Cable.xcworkspace/contents.xcworkspacedata +++ b/Cable.xcworkspace/contents.xcworkspacedata @@ -4,7 +4,4 @@ - - diff --git a/Cable/AppDelegate.swift b/Cable/AppDelegate.swift index a8ee51e..809b3a9 100644 --- a/Cable/AppDelegate.swift +++ b/Cable/AppDelegate.swift @@ -7,18 +7,30 @@ import Foundation -import PostHog import UIKit class AppDelegate: NSObject, UIApplicationDelegate { func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { - let POSTHOG_API_KEY = "phc_icZY61N3vdg4Sr3lzz9DNAqCRh6hCorVJbytduWORO9" - let POSTHOG_HOST = "https://eu.i.posthog.com" - - let config = PostHogConfig(apiKey: POSTHOG_API_KEY, host: POSTHOG_HOST) - - PostHogSDK.shared.setup(config) + AnalyticsTracker.configure() NSLog("Launched") return true } } + +enum AnalyticsTracker { + static func configure() {} + + static func log(_ event: String, properties: [String: Any] = [:]) { +#if DEBUG + if properties.isEmpty { + NSLog("Analytics: %@", event) + } else { + let formatted = properties + .map { "\($0.key)=\($0.value)" } + .sorted() + .joined(separator: ", ") + NSLog("Analytics: %@ { %@ }", event, formatted) + } +#endif + } +} diff --git a/Cable/Base.lproj/Localizable.strings b/Cable/Base.lproj/Localizable.strings index 65c7b77..2dc729b 100644 --- a/Cable/Base.lproj/Localizable.strings +++ b/Cable/Base.lproj/Localizable.strings @@ -84,6 +84,29 @@ "bom.navigation.title.system" = "BOM – %@"; "bom.size.unknown" = "Size TBD"; "bom.terminals.detail" = "Ring or spade terminals sized for %@ wiring"; +"bom.empty.message" = "No components saved in this system yet."; +"bom.export.pdf.button" = "Export PDF"; +"bom.export.pdf.error.title" = "Export Failed"; +"bom.export.pdf.error.empty" = "Add at least one component before exporting."; +"bom.pdf.header.title" = "System Bill of Materials"; +"bom.pdf.header.subtitle" = "%@ • %@"; +"bom.pdf.header.inline" = "Unit System: %@"; +"bom.pdf.placeholder.empty" = "No components available."; +"bom.pdf.page.number" = "Page %d"; +"bom.category.components.title" = "Components & Chargers"; +"bom.category.components.subtitle" = "Primary devices, controllers, and charging gear."; +"bom.category.batteries.title" = "Batteries"; +"bom.category.batteries.subtitle" = "House banks and storage."; +"bom.category.cables.title" = "Cables"; +"bom.category.cables.subtitle" = "Sized power runs for every circuit."; +"bom.category.fuses.title" = "Fuses"; +"bom.category.fuses.subtitle" = "Circuit protection and holders."; +"bom.category.accessories.title" = "Accessories"; +"bom.category.accessories.subtitle" = "Fuses, lugs, and supporting hardware."; +"bom.cable.detail.quantified" = "%1$dx %2$@"; +"bom.quantity.count.badge" = "%d×"; +"bom.quantity.length.badge" = "%1$.1f %2$@"; +"bom.quantity.length.badge.with.spec" = "%1$.1f %2$@ · %3$@"; "cable.pro.privacy.label" = "Privacy"; "cable.pro.privacy.url" = "https://voltplan.app/privacy"; "cable.pro.terms.label" = "Terms"; @@ -277,6 +300,7 @@ "cable.pro.alert.restored.body" = "Your purchases are available again."; "cable.pro.alert.error.title" = "Purchase Failed"; "cable.pro.alert.error.generic" = "Something went wrong. Please try again."; +"generic.ok" = "OK"; "cable.pro.trial.badge" = "Includes a %@ free trial"; "cable.pro.subscription.renews" = "Renews %@."; "cable.pro.subscription.trialThenRenews" = "Free trial, then renews %@."; diff --git a/Cable/CableApp.swift b/Cable/CableApp.swift index b651c32..2a7126b 100644 --- a/Cable/CableApp.swift +++ b/Cable/CableApp.swift @@ -37,7 +37,7 @@ struct CableApp: App { _unitSettings = StateObject(wrappedValue: unitSettings) _storeKitManager = StateObject(wrappedValue: StoreKitManager(unitSettings: unitSettings)) #if DEBUG - UITestSampleData.prepareIfNeeded(container: sharedModelContainer) + UITestSampleData.handleLaunchArguments(container: sharedModelContainer) #endif } diff --git a/Cable/Chargers/SavedCharger.swift b/Cable/Chargers/SavedCharger.swift index 05aaadf..5f383c2 100644 --- a/Cable/Chargers/SavedCharger.swift +++ b/Cable/Chargers/SavedCharger.swift @@ -16,6 +16,7 @@ final class SavedCharger { var remoteIconURLString: String? var affiliateURLString: String? var affiliateCountryCode: String? + var bomCompletedItemIDs: [String] = [] var identifier: String init( @@ -32,6 +33,7 @@ final class SavedCharger { remoteIconURLString: String? = nil, affiliateURLString: String? = nil, affiliateCountryCode: String? = nil, + bomCompletedItemIDs: [String] = [], identifier: String = UUID().uuidString ) { self.id = id @@ -47,6 +49,7 @@ final class SavedCharger { self.remoteIconURLString = remoteIconURLString self.affiliateURLString = affiliateURLString self.affiliateCountryCode = affiliateCountryCode + self.bomCompletedItemIDs = bomCompletedItemIDs self.identifier = identifier } diff --git a/Cable/Loads/CableCalculator.swift b/Cable/Loads/CableCalculator.swift index 0a267b7..5850053 100644 --- a/Cable/Loads/CableCalculator.swift +++ b/Cable/Loads/CableCalculator.swift @@ -34,31 +34,12 @@ class CableCalculator: ObservableObject { } func recommendedCrossSection(for unitSystem: UnitSystem) -> Double { - let lengthInMeters = unitSystem == .metric ? length : length * 0.3048 // ft to m - // Simplified calculation: minimum cross-section based on current and voltage drop - let maxVoltageDrop = voltage * 0.05 // 5% voltage drop limit - let resistivity = 0.017 // Copper resistivity at 20°C (Ω⋅mm²/m) - let calculatedMinCrossSection = (2 * current * lengthInMeters * resistivity) / maxVoltageDrop - - if unitSystem == .imperial { - // Standard AWG wire sizes - let standardAWG = [20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, 0, 00, 000, 0000] - let awgCrossSections = [0.519, 0.823, 1.31, 2.08, 3.31, 5.26, 8.37, 13.3, 21.2, 33.6, 42.4, 53.5, 67.4, 85.0, 107.0] - - // Find the smallest AWG that meets the requirement - for (index, crossSection) in awgCrossSections.enumerated() { - if crossSection >= calculatedMinCrossSection { - return Double(standardAWG[index]) - } - } - return Double(standardAWG.last!) // Largest available - } else { - // Standard metric cable cross-sections in mm² - let standardSizes = [0.75, 1.0, 1.5, 2.5, 4.0, 6.0, 10.0, 16.0, 25.0, 35.0, 50.0, 70.0, 95.0, 120.0, 150.0, 185.0, 240.0, 300.0, 400.0, 500.0, 630.0] - - // Find the smallest standard size that meets the requirement - return standardSizes.first { $0 >= max(0.75, calculatedMinCrossSection) } ?? standardSizes.last! - } + ElectricalCalculations.recommendedCrossSection( + length: length, + current: current, + voltage: voltage, + unitSystem: unitSystem + ) } func crossSection(for unitSystem: UnitSystem) -> Double { @@ -66,42 +47,34 @@ class CableCalculator: ObservableObject { } func voltageDrop(for unitSystem: UnitSystem) -> Double { - let lengthInMeters = unitSystem == .metric ? length : length * 0.3048 - let crossSectionMM2 = unitSystem == .metric ? crossSection(for: unitSystem) : crossSectionFromAWG(crossSection(for: unitSystem)) - let resistivity = 0.017 - let effectiveCurrent = current // Always use the current property which gets updated - return (2 * effectiveCurrent * lengthInMeters * resistivity) / crossSectionMM2 + ElectricalCalculations.voltageDrop( + length: length, + current: current, + voltage: voltage, + unitSystem: unitSystem + ) } func voltageDropPercentage(for unitSystem: UnitSystem) -> Double { - (voltageDrop(for: unitSystem) / voltage) * 100 + ElectricalCalculations.voltageDropPercentage( + length: length, + current: current, + voltage: voltage, + unitSystem: unitSystem + ) } func powerLoss(for unitSystem: UnitSystem) -> Double { - let effectiveCurrent = current - return effectiveCurrent * voltageDrop(for: unitSystem) + ElectricalCalculations.powerLoss( + length: length, + current: current, + voltage: voltage, + unitSystem: unitSystem + ) } var recommendedFuse: Int { - let targetFuse = current * 1.25 // 125% of load current for safety - - // Common fuse values in amperes - let standardFuses = [1, 2, 3, 5, 7, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500, 600, 700, 800] - - // Find the smallest standard fuse that's >= target - return standardFuses.first { $0 >= Int(targetFuse.rounded(.up)) } ?? standardFuses.last! - } - - // AWG conversion helper for voltage drop calculations - private func crossSectionFromAWG(_ awg: Double) -> Double { - let awgSizes = [20: 0.519, 18: 0.823, 16: 1.31, 14: 2.08, 12: 3.31, 10: 5.26, 8: 8.37, 6: 13.3, 4: 21.2, 2: 33.6, 1: 42.4, 0: 53.5] - - // Handle 00, 000, 0000 AWG (represented as negative values) - if awg == 00 { return 67.4 } - if awg == 000 { return 85.0 } - if awg == 0000 { return 107.0 } - - return awgSizes[Int(awg)] ?? 0.75 + ElectricalCalculations.recommendedFuse(forCurrent: current) } } diff --git a/Cable/Loads/ElectricalCalculations.swift b/Cable/Loads/ElectricalCalculations.swift new file mode 100644 index 0000000..3da8bdb --- /dev/null +++ b/Cable/Loads/ElectricalCalculations.swift @@ -0,0 +1,138 @@ +// +// ElectricalCalculations.swift +// Cable +// +// Created by GPT on request. +// + +import Foundation + +struct ElectricalCalculations { + private static let maxVoltageDropFraction = 0.05 + private static let copperResistivity = 0.017 // Ω⋅mm²/m + private static let feetToMeters = 0.3048 + + private static let standardMetricCrossSections: [Double] = [ + 0.75, 1.0, 1.5, 2.5, 4.0, 6.0, 10.0, 16.0, 25.0, 35.0, 50.0, 70.0, 95.0, 120.0, + 150.0, 185.0, 240.0, 300.0, 400.0, 500.0, 630.0, + ] + + private static let standardAWG: [Int] = [20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, 0, 00, 000, 0000] + private static let awgCrossSections: [Double] = [ + 0.519, 0.823, 1.31, 2.08, 3.31, 5.26, 8.37, 13.3, 21.2, 33.6, 42.4, 53.5, 67.4, 85.0, 107.0, + ] + + private static let standardFuses: [Int] = [ + 1, 2, 3, 5, 7, 10, 15, 20, 25, 30, 35, 40, 50, + 60, 70, 80, 100, 125, 150, 175, 200, 225, 250, + 300, 350, 400, 450, 500, 600, 700, 800, + ] + + static func recommendedCrossSection( + length: Double, + current: Double, + voltage: Double, + unitSystem: UnitSystem + ) -> Double { + let lengthInMeters = unitSystem == .metric ? length : length * feetToMeters + let maxVoltageDrop = voltage * maxVoltageDropFraction + let minimumCrossSection = guardAgainstZero(maxVoltageDrop) { + (2 * current * lengthInMeters * copperResistivity) / maxVoltageDrop + } + + if unitSystem == .imperial { + for (index, crossSection) in awgCrossSections.enumerated() where crossSection >= minimumCrossSection { + return Double(standardAWG[index]) + } + return Double(standardAWG.last ?? 0) + } else { + return standardMetricCrossSections.first { $0 >= max(standardMetricCrossSections.first ?? 0.75, minimumCrossSection) } + ?? standardMetricCrossSections.last ?? 0.75 + } + } + + static func voltageDrop( + length: Double, + current: Double, + voltage: Double, + unitSystem: UnitSystem, + crossSection: Double? = nil + ) -> Double { + let selectedCrossSection = crossSection ?? recommendedCrossSection( + length: length, + current: current, + voltage: voltage, + unitSystem: unitSystem + ) + + let lengthInMeters = unitSystem == .metric ? length : length * feetToMeters + let crossSectionMM2: Double + if unitSystem == .metric { + crossSectionMM2 = selectedCrossSection + } else { + crossSectionMM2 = crossSectionFromAWG(selectedCrossSection) + } + + guard crossSectionMM2 > 0 else { return 0 } + return (2 * current * lengthInMeters * copperResistivity) / crossSectionMM2 + } + + static func voltageDropPercentage( + length: Double, + current: Double, + voltage: Double, + unitSystem: UnitSystem, + crossSection: Double? = nil + ) -> Double { + guard voltage != 0 else { return 0 } + let drop = voltageDrop( + length: length, + current: current, + voltage: voltage, + unitSystem: unitSystem, + crossSection: crossSection + ) + return (drop / voltage) * 100 + } + + static func powerLoss( + length: Double, + current: Double, + voltage: Double, + unitSystem: UnitSystem, + crossSection: Double? = nil + ) -> Double { + let drop = voltageDrop( + length: length, + current: current, + voltage: voltage, + unitSystem: unitSystem, + crossSection: crossSection + ) + return current * drop + } + + static func recommendedFuse(forCurrent current: Double) -> Int { + let target = Int((current * 1.25).rounded(.up)) + return standardFuses.first(where: { $0 >= target }) ?? standardFuses.last ?? target + } + + private static func guardAgainstZero(_ divisor: Double, calculation: () -> Double) -> Double { + guard divisor > 0 else { return 0 } + return calculation() + } + + private static func crossSectionFromAWG(_ awg: Double) -> Double { + switch awg { + case 00: return 67.4 + case 000: return 85.0 + case 0000: return 107.0 + default: + let index = standardAWG.firstIndex(of: Int(awg)) ?? -1 + if index >= 0 && index < awgCrossSections.count { + return awgCrossSections[index] + } + return 0.75 + } + } +} diff --git a/Cable/Loads/LoadsView.swift b/Cable/Loads/LoadsView.swift index 7cea401..fa7c00b 100644 --- a/Cable/Loads/LoadsView.swift +++ b/Cable/Loads/LoadsView.swift @@ -8,7 +8,6 @@ import SwiftUI import SwiftData -import PostHog struct LoadsView: View { @Environment(\.modelContext) private var modelContext @@ -64,6 +63,7 @@ struct LoadsView: View { ), systemImage: "rectangle.3.group" ) + .accessibilityIdentifier("overview-tab") } componentsTab @@ -77,6 +77,7 @@ struct LoadsView: View { ), systemImage: "square.stack.3d.up" ) + .accessibilityIdentifier("components-tab") } Group { @@ -106,6 +107,7 @@ struct LoadsView: View { ), systemImage: "battery.100" ) + .accessibilityIdentifier("batteries-tab") } .environment(\.editMode, $editMode) @@ -127,6 +129,7 @@ struct LoadsView: View { ), systemImage: "bolt.fill" ) + .accessibilityIdentifier("chargers-tab") } .environment(\.editMode, $editMode) } @@ -215,6 +218,8 @@ struct LoadsView: View { SystemBillOfMaterialsView( systemName: system.name, loads: savedLoads, + batteries: savedBatteries, + chargers: savedChargers, unitSystem: unitSettings.unitSystem ) } @@ -266,7 +271,7 @@ struct LoadsView: View { if let loadToOpen = loadToOpenOnAppear, !hasOpenedLoadOnAppear { hasOpenedLoadOnAppear = true DispatchQueue.main.async { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Load Opened", properties: [ "mode": loadToOpen.isWattMode ? "watt" : "amp", @@ -469,7 +474,7 @@ struct LoadsView: View { } private func selectLoad(_ load: SavedLoad) { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Load Opened", properties: [ "mode": load.isWattMode ? "watt" : "amp", @@ -760,7 +765,7 @@ struct LoadsView: View { } private func presentSystemEditor(source: String) { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "System Editor Opened", properties: [ "source": source, @@ -771,7 +776,7 @@ struct LoadsView: View { } private func openComponentLibrary(source: String) { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Component Library Opened", properties: [ "source": source, @@ -782,7 +787,7 @@ struct LoadsView: View { } private func openBillOfMaterials() { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Bill Of Materials Opened", properties: [ "system": system.name @@ -795,7 +800,7 @@ struct LoadsView: View { let loadsToDelete = offsets.map { savedLoads[$0] } withAnimation { for load in loadsToDelete { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Load Deleted", properties: [ "name": load.name, @@ -815,7 +820,7 @@ struct LoadsView: View { existingBatteries: savedBatteries, existingChargers: savedChargers ) - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Load Created", properties: [ "name": newLoad.name, @@ -826,7 +831,7 @@ struct LoadsView: View { } private func startBatteryConfiguration() { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Battery Editor Opened", properties: [ "source": "create", @@ -850,7 +855,7 @@ struct LoadsView: View { in: modelContext ) let eventName = isExisting ? "Battery Updated" : "Battery Created" - PostHogSDK.shared.capture( + AnalyticsTracker.log( eventName, properties: [ "name": configuration.name, @@ -860,7 +865,7 @@ struct LoadsView: View { } private func editBattery(_ battery: SavedBattery) { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Battery Editor Opened", properties: [ "source": "edit", @@ -874,7 +879,7 @@ struct LoadsView: View { let batteriesToDelete = offsets.map { savedBatteries[$0] } withAnimation { for battery in batteriesToDelete { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Battery Deleted", properties: [ "name": battery.name, @@ -891,7 +896,7 @@ struct LoadsView: View { } private func startChargerConfiguration() { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Charger Editor Opened", properties: [ "source": "create", @@ -915,7 +920,7 @@ struct LoadsView: View { in: modelContext ) let eventName = isExisting ? "Charger Updated" : "Charger Created" - PostHogSDK.shared.capture( + AnalyticsTracker.log( eventName, properties: [ "name": configuration.name, @@ -925,7 +930,7 @@ struct LoadsView: View { } private func editCharger(_ charger: SavedCharger) { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Charger Editor Opened", properties: [ "source": "edit", @@ -939,7 +944,7 @@ struct LoadsView: View { let chargersToDelete = offsets.map { savedChargers[$0] } withAnimation { for charger in chargersToDelete { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Charger Deleted", properties: [ "name": charger.name, @@ -964,7 +969,7 @@ struct LoadsView: View { existingBatteries: savedBatteries, existingChargers: savedChargers ) - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Library Load Added", properties: [ "id": item.id, @@ -986,13 +991,7 @@ struct LoadsView: View { } private func recommendedFuse(for load: SavedLoad) -> Int { - let targetFuse = load.current * 1.25 // 125% of load current for safety - - // Common fuse values in amperes - let standardFuses = [1, 2, 3, 5, 7, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500, 600, 700, 800] - - // Find the smallest standard fuse that's >= target - return standardFuses.first { $0 >= Int(targetFuse.rounded(.up)) } ?? standardFuses.last! + ElectricalCalculations.recommendedFuse(forCurrent: load.current) } private enum ComponentTab: Hashable { diff --git a/Cable/Loads/OnboardingInfoView.swift b/Cable/Loads/OnboardingInfoView.swift index ccb4fa0..db53258 100644 --- a/Cable/Loads/OnboardingInfoView.swift +++ b/Cable/Loads/OnboardingInfoView.swift @@ -62,6 +62,7 @@ struct OnboardingInfoView: View { Label(configuration.primaryActionTitle, systemImage: configuration.primaryActionIcon) .frame(maxWidth: .infinity) } + .accessibilityIdentifier("create-component-button") .buttonStyle(.borderedProminent) .controlSize(.large) @@ -71,6 +72,7 @@ struct OnboardingInfoView: View { Label(secondaryTitle, systemImage: secondaryIcon) .frame(maxWidth: .infinity) } + .accessibilityIdentifier("select-component-button") .buttonStyle(.bordered) .tint(.accentColor) .controlSize(.large) diff --git a/Cable/Overview/SystemOverviewView.swift b/Cable/Overview/SystemOverviewView.swift index 7c41db2..aefd457 100644 --- a/Cable/Overview/SystemOverviewView.swift +++ b/Cable/Overview/SystemOverviewView.swift @@ -159,8 +159,10 @@ struct SystemOverviewView: View { goalHours: nil, progressFraction: bomCompletionFraction, hasValue: bomItemsCount > 0, - action: onShowBillOfMaterials + action: onShowBillOfMaterials, + accessibilityIdentifier: "system-bom-button" ) + } .padding(.top, 4) } @@ -190,7 +192,8 @@ struct SystemOverviewView: View { goalHours: Double?, progressFraction: Double?, hasValue: Bool, - action: (() -> Void)? = nil + action: (() -> Void)? = nil, + accessibilityIdentifier: String? = nil ) -> some View { let content = VStack(alignment: .leading, spacing: 10) { HStack(alignment: .center, spacing: 12) { @@ -240,12 +243,28 @@ struct SystemOverviewView: View { let paddedContent = content .padding(.horizontal, 4) + .frame(maxWidth: .infinity, alignment: .leading) + .contentShape(Rectangle()) if let action { - Button(action: action) { - paddedContent + if let accessibilityIdentifier { + Button(action: action) { + paddedContent + } + .buttonStyle(.plain) + .accessibilityIdentifier(accessibilityIdentifier) + .accessibilityLabel(title) + .accessibilityAddTraits(.isButton) + .contentShape(Rectangle()) + } else { + Button(action: action) { + paddedContent + } + .buttonStyle(.plain) + .accessibilityLabel(title) + .accessibilityAddTraits(.isButton) + .contentShape(Rectangle()) } - .buttonStyle(.plain) } else { paddedContent } diff --git a/Cable/SavedBattery.swift b/Cable/SavedBattery.swift index 4a75710..06215e7 100644 --- a/Cable/SavedBattery.swift +++ b/Cable/SavedBattery.swift @@ -16,6 +16,7 @@ class SavedBattery { var iconName: String = "battery.100" var colorName: String = "blue" var system: ElectricalSystem? + var bomCompletedItemIDs: [String] = [] var timestamp: Date init( @@ -32,6 +33,7 @@ class SavedBattery { iconName: String = "battery.100", colorName: String = "blue", system: ElectricalSystem? = nil, + bomCompletedItemIDs: [String] = [], timestamp: Date = Date() ) { self.id = id @@ -47,6 +49,7 @@ class SavedBattery { self.iconName = iconName self.colorName = colorName self.system = system + self.bomCompletedItemIDs = bomCompletedItemIDs self.timestamp = timestamp } diff --git a/Cable/Shared/ShareSheet.swift b/Cable/Shared/ShareSheet.swift new file mode 100644 index 0000000..45f5143 --- /dev/null +++ b/Cable/Shared/ShareSheet.swift @@ -0,0 +1,11 @@ +import SwiftUI + +struct ShareSheet: UIViewControllerRepresentable { + let items: [Any] + + func makeUIViewController(context: Context) -> UIActivityViewController { + UIActivityViewController(activityItems: items, applicationActivities: nil) + } + + func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} +} diff --git a/Cable/Systems/BillOfMaterialsSnapshot.swift b/Cable/Systems/BillOfMaterialsSnapshot.swift new file mode 100644 index 0000000..64ea509 --- /dev/null +++ b/Cable/Systems/BillOfMaterialsSnapshot.swift @@ -0,0 +1,17 @@ +import Foundation + +struct BillOfMaterialsItemSnapshot: Identifiable { + let id: String + let title: String + let detail: String + let iconSystemName: String + let isPrimaryComponent: Bool + let metric: String? +} + +struct BillOfMaterialsSectionSnapshot: Identifiable { + let id: String + let title: String + let subtitle: String + let items: [BillOfMaterialsItemSnapshot] +} diff --git a/Cable/Systems/SystemBillOfMaterialsPDFExporter.swift b/Cable/Systems/SystemBillOfMaterialsPDFExporter.swift new file mode 100644 index 0000000..4a09053 --- /dev/null +++ b/Cable/Systems/SystemBillOfMaterialsPDFExporter.swift @@ -0,0 +1,313 @@ +import Foundation +import UIKit + +struct SystemBillOfMaterialsPDFExporter { + private let pageRect = CGRect(x: 0, y: 0, width: 595, height: 842) // A4 portrait in points + private let margin: CGFloat = 40 + private let primaryTextColor = UIColor.black + private let secondaryTextColor = UIColor.darkGray + private let tertiaryTextColor = UIColor.gray + private let accentColor = UIColor(red: 0.45, green: 0.34, blue: 0.86, alpha: 1) + + func export( + systemName: String, + unitSystem: UnitSystem, + sections: [BillOfMaterialsSectionSnapshot] + ) throws -> URL { + let format = UIGraphicsPDFRendererFormat() + let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: format) + var pageIndex = 1 + + let data = renderer.pdfData { context in + var cursorY = beginPage( + context: context, + pageIndex: pageIndex, + systemName: systemName, + unitSystem: unitSystem, + isFirstPage: true + ) + + if sections.isEmpty { + cursorY = ensureSpace( + requiredHeight: 60, + cursorY: cursorY, + context: context, + pageIndex: &pageIndex, + systemName: systemName, + unitSystem: unitSystem + ) + let emptyMessage = NSLocalizedString( + "bom.pdf.placeholder.empty", + comment: "Message shown in the PDF export when no components are available" + ) + drawPlaceholder(in: context.cgContext, text: emptyMessage, at: cursorY) + } else { + for section in sections { + let requiredHeight = sectionHeight(for: section) + cursorY = ensureSpace( + requiredHeight: requiredHeight, + cursorY: cursorY, + context: context, + pageIndex: &pageIndex, + systemName: systemName, + unitSystem: unitSystem + ) + + cursorY = drawSectionHeader( + title: section.title, + subtitle: section.subtitle, + at: cursorY, + in: context.cgContext + ) + + for item in section.items { + cursorY = drawItem(item, at: cursorY, in: context.cgContext) + cursorY += 12 + } + + cursorY += 8 + } + } + + drawFooter(pageIndex: pageIndex, in: context.cgContext) + } + + let url = FileManager.default.temporaryDirectory + .appendingPathComponent("System-BOM-\(UUID().uuidString).pdf") + try data.write(to: url, options: .atomic) + return url + } + + private func beginPage( + context: UIGraphicsPDFRendererContext, + pageIndex: Int, + systemName: String, + unitSystem: UnitSystem, + isFirstPage: Bool + ) -> CGFloat { + context.beginPage() + let titleFont = UIFont.systemFont(ofSize: isFirstPage ? 26 : 18, weight: .bold) + let subtitleFont = UIFont.systemFont(ofSize: isFirstPage ? 16 : 12, weight: .medium) + let title = isFirstPage + ? NSLocalizedString( + "bom.pdf.header.title", + comment: "Primary title shown at the top of the BOM PDF" + ) + : systemName + + let subtitle: String + if isFirstPage { + let format = NSLocalizedString( + "bom.pdf.header.subtitle", + comment: "Subtitle format combining system name and unit system for the BOM PDF" + ) + subtitle = String( + format: format, + locale: Locale.current, + systemName, + unitSystem.displayName + ) + } else { + let format = NSLocalizedString( + "bom.pdf.header.inline", + comment: "Subtitle describing the active unit system on subsequent PDF pages" + ) + subtitle = String( + format: format, + locale: Locale.current, + unitSystem.displayName + ) + } + + let availableWidth = pageRect.width - (margin * 2) + let titleRect = CGRect(x: margin, y: margin, width: availableWidth, height: titleFont.lineHeight + 4) + title.draw(in: titleRect, withAttributes: [ + .font: titleFont, + .foregroundColor: primaryTextColor + ]) + + let subtitleRect = CGRect( + x: margin, + y: titleRect.maxY + 4, + width: availableWidth, + height: subtitleFont.lineHeight + 2 + ) + subtitle.draw(in: subtitleRect, withAttributes: [ + .font: subtitleFont, + .foregroundColor: secondaryTextColor + ]) + + return subtitleRect.maxY + (isFirstPage ? 24 : 12) + } + + private func ensureSpace( + requiredHeight: CGFloat, + cursorY: CGFloat, + context: UIGraphicsPDFRendererContext, + pageIndex: inout Int, + systemName: String, + unitSystem: UnitSystem + ) -> CGFloat { + if cursorY + requiredHeight <= pageRect.height - margin { + return cursorY + } + + drawFooter(pageIndex: pageIndex, in: context.cgContext) + pageIndex += 1 + return beginPage( + context: context, + pageIndex: pageIndex, + systemName: systemName, + unitSystem: unitSystem, + isFirstPage: false + ) + } + + private var sectionHeaderHeight: CGFloat { + let headerFont = UIFont.systemFont(ofSize: 18, weight: .semibold) + let subtitleFont = UIFont.systemFont(ofSize: 12, weight: .medium) + return headerFont.lineHeight + subtitleFont.lineHeight + 14 + } + + private func sectionHeight(for section: BillOfMaterialsSectionSnapshot) -> CGFloat { + let itemsHeight = section.items.reduce(0) { partialResult, item in + partialResult + itemBlockHeight(for: item) + 12 + } + return sectionHeaderHeight + itemsHeight + 8 + } + + private func drawSectionHeader(title: String, subtitle: String, at yPosition: CGFloat, in context: CGContext) -> CGFloat { + var cursorY = yPosition + let headerFont = UIFont.systemFont(ofSize: 18, weight: .semibold) + let subtitleFont = UIFont.systemFont(ofSize: 12, weight: .medium) + let availableWidth = pageRect.width - (margin * 2) + + title.draw( + in: CGRect(x: margin, y: cursorY, width: availableWidth, height: headerFont.lineHeight + 4), + withAttributes: [ + .font: headerFont, + .foregroundColor: primaryTextColor + ] + ) + cursorY += headerFont.lineHeight + 4 + + subtitle.draw( + in: CGRect(x: margin, y: cursorY, width: availableWidth, height: subtitleFont.lineHeight + 2), + withAttributes: [ + .font: subtitleFont, + .foregroundColor: secondaryTextColor + ] + ) + cursorY += subtitleFont.lineHeight + 10 + + return cursorY + } + + private func itemBlockHeight(for item: BillOfMaterialsItemSnapshot) -> CGFloat { + let metricFont = UIFont.systemFont(ofSize: 13, weight: .semibold) + let titleFont = UIFont.systemFont(ofSize: 14, weight: item.isPrimaryComponent ? .semibold : .medium) + let detailFont = UIFont.systemFont(ofSize: 12, weight: .regular) + var height: CGFloat = 0 + if item.metric != nil { + height += metricFont.lineHeight + 2 + } + height += titleFont.lineHeight + 2 + if !item.detail.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + height += detailFont.lineHeight + 4 + } + return height + 4 + } + + private func drawItem(_ item: BillOfMaterialsItemSnapshot, at yPosition: CGFloat, in context: CGContext) -> CGFloat { + let metricFont = UIFont.systemFont(ofSize: 13, weight: .semibold) + let titleFont = UIFont.systemFont(ofSize: 14, weight: item.isPrimaryComponent ? .semibold : .medium) + let detailFont = UIFont.systemFont(ofSize: 12, weight: .regular) + let titleAttributes: [NSAttributedString.Key: Any] = [ + .font: titleFont, + .foregroundColor: primaryTextColor + ] + let detailAttributes: [NSAttributedString.Key: Any] = [ + .font: detailFont, + .foregroundColor: secondaryTextColor + ] + let metricAttributes: [NSAttributedString.Key: Any] = [ + .font: metricFont, + .foregroundColor: accentColor + ] + + let bulletWidth: CGFloat = 6 + let spacing: CGFloat = 8 + let availableWidth = pageRect.width - (margin * 2) - bulletWidth - spacing + let firstLineHeight = item.metric != nil ? metricFont.lineHeight : titleFont.lineHeight + let bulletRect = CGRect( + x: margin, + y: yPosition + (firstLineHeight / 2) - (bulletWidth / 2), + width: bulletWidth, + height: bulletWidth + ) + context.setFillColor(accentColor.cgColor) + context.fillEllipse(in: bulletRect) + + var cursorY = yPosition + let textX = margin + bulletWidth + spacing + + if let metric = item.metric { + let metricRect = CGRect(x: textX, y: cursorY, width: availableWidth, height: metricFont.lineHeight + 2) + metric.draw(in: metricRect, withAttributes: metricAttributes) + cursorY = metricRect.maxY + 2 + } + + let titleRect = CGRect( + x: textX, + y: cursorY, + width: availableWidth, + height: titleFont.lineHeight + 2 + ) + item.title.draw(in: titleRect, withAttributes: titleAttributes) + cursorY = titleRect.maxY + 2 + + if !item.detail.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + let detailRect = CGRect( + x: textX, + y: cursorY, + width: availableWidth, + height: detailFont.lineHeight + 4 + ) + item.detail.draw(in: detailRect, withAttributes: detailAttributes) + cursorY = detailRect.maxY + } + + return cursorY + } + + private func drawFooter(pageIndex: Int, in context: CGContext) { + let footerFont = UIFont.systemFont(ofSize: 11, weight: .regular) + let attributes: [NSAttributedString.Key: Any] = [ + .font: footerFont, + .foregroundColor: tertiaryTextColor + ] + let format = NSLocalizedString( + "bom.pdf.page.number", + comment: "Format string for the PDF page number footer" + ) + let text = String(format: format, locale: Locale.current, pageIndex) + let size = text.size(withAttributes: attributes) + let origin = CGPoint( + x: (pageRect.width - size.width) / 2, + y: pageRect.height - margin + 10 + ) + text.draw(at: origin, withAttributes: attributes) + } + + private func drawPlaceholder(in context: CGContext, text: String, at yPosition: CGFloat) { + let font = UIFont.systemFont(ofSize: 14, weight: .regular) + let attributes: [NSAttributedString.Key: Any] = [ + .font: font, + .foregroundColor: secondaryTextColor + ] + text.draw( + in: CGRect(x: margin, y: yPosition, width: pageRect.width - (margin * 2), height: font.lineHeight + 4), + withAttributes: attributes + ) + } +} diff --git a/Cable/Systems/SystemBillOfMaterialsView.swift b/Cable/Systems/SystemBillOfMaterialsView.swift index e840d6c..fd65c34 100644 --- a/Cable/Systems/SystemBillOfMaterialsView.swift +++ b/Cable/Systems/SystemBillOfMaterialsView.swift @@ -12,12 +12,132 @@ import SwiftData struct SystemBillOfMaterialsView: View { let systemName: String let loads: [SavedLoad] + let batteries: [SavedBattery] + let chargers: [SavedCharger] let unitSystem: UnitSystem @Environment(\.dismiss) private var dismiss @Environment(\.openURL) private var openURL @State private var completedItemIDs: Set @State private var suppressRowTapForID: String? + @State private var isExportingPDF = false + @State private var activeShareItem: ExportedPDFShareItem? + @State private var exportError: ExportError? + + private enum Component { + case load(SavedLoad) + case battery(SavedBattery) + case charger(SavedCharger) + } + + private enum Category: String, CaseIterable { + case components + case batteries + case cables + case fuses + case accessories + + var title: String { + switch self { + case .components: + return NSLocalizedString( + "bom.category.components.title", + comment: "Section title for core components and chargers" + ) + case .batteries: + return NSLocalizedString( + "bom.category.batteries.title", + comment: "Section title for batteries" + ) + case .cables: + return NSLocalizedString( + "bom.category.cables.title", + comment: "Section title for power cables" + ) + case .fuses: + return NSLocalizedString( + "bom.category.fuses.title", + comment: "Section title for fuses and holders" + ) + case .accessories: + return NSLocalizedString( + "bom.category.accessories.title", + comment: "Section title for accessory hardware" + ) + } + } + + var subtitle: String { + switch self { + case .components: + return NSLocalizedString( + "bom.category.components.subtitle", + comment: "Subtitle describing the components section" + ) + case .batteries: + return NSLocalizedString( + "bom.category.batteries.subtitle", + comment: "Subtitle describing the batteries section" + ) + case .cables: + return NSLocalizedString( + "bom.category.cables.subtitle", + comment: "Subtitle describing the cables section" + ) + case .fuses: + return NSLocalizedString( + "bom.category.fuses.subtitle", + comment: "Subtitle describing the fuses section" + ) + case .accessories: + return NSLocalizedString( + "bom.category.accessories.subtitle", + comment: "Subtitle describing the accessories section" + ) + } + } + + static var ordered: [Category] { + [.components, .batteries, .cables, .fuses, .accessories] + } + } + + private struct CategorySection: Identifiable { + let category: Category + let items: [Item] + + var id: String { category.rawValue } + var title: String { category.title } + var subtitle: String { category.subtitle } + + var snapshot: BillOfMaterialsSectionSnapshot { + BillOfMaterialsSectionSnapshot( + id: id, + title: title, + subtitle: subtitle, + items: items.map { item in + BillOfMaterialsItemSnapshot( + id: item.id, + title: item.title, + detail: item.detail, + iconSystemName: item.iconSystemName, + isPrimaryComponent: item.isPrimaryComponent, + metric: item.primaryMetricText + ) + } + ) + } + } + + private struct ExportedPDFShareItem: Identifiable { + let url: URL + var id: URL { url } + } + + private struct ExportError: Identifiable { + let id = UUID() + let message: String + } private struct Item: Identifiable { enum Destination { @@ -26,21 +146,115 @@ struct SystemBillOfMaterialsView: View { } let id: String + let storageKeys: [String] let logicalID: String let title: String let detail: String let iconSystemName: String let destination: Destination let isPrimaryComponent: Bool + let components: [Component] + let category: Category + let quantity: Int + let mergeKey: String? + let quantifiedDetailContext: String? + let quantifiedDetailSecondaryContext: String? + let quantityScale: Double? + + var primaryMetricText: String? { + if let scale = quantityScale { + guard let unit = quantifiedDetailContext else { return nil } + let value = Double(quantity) / scale + if let spec = quantifiedDetailSecondaryContext { + let format = NSLocalizedString( + "bom.quantity.length.badge.with.spec", + comment: "Badge text for total cable length including cross section" + ) + return String(format: format, locale: Locale.current, value, unit, spec) + } else { + let format = NSLocalizedString( + "bom.quantity.length.badge", + comment: "Badge text for total cable length" + ) + return String(format: format, locale: Locale.current, value, unit) + } + } else if quantity > 1 { + let format = NSLocalizedString( + "bom.quantity.count.badge", + comment: "Badge text for quantity counts" + ) + return String(format: format, quantity) + } + return nil + } + + var primaryComponent: Component { + components.first ?? components.last! + } + + func updatingQuantity(_ quantity: Int, components newComponents: [Component], storageKeys newStorageKeys: [String]) -> Item { + Item( + id: id, + storageKeys: newStorageKeys, + logicalID: logicalID, + title: title, + detail: detail, + iconSystemName: iconSystemName, + destination: destination, + isPrimaryComponent: isPrimaryComponent, + components: newComponents, + category: category, + quantity: quantity, + mergeKey: mergeKey, + quantifiedDetailContext: quantifiedDetailContext, + quantifiedDetailSecondaryContext: quantifiedDetailSecondaryContext, + quantityScale: quantityScale + ) + } + + func replacingID(_ newID: String) -> Item { + Item( + id: newID, + storageKeys: storageKeys, + logicalID: logicalID, + title: title, + detail: detail, + iconSystemName: iconSystemName, + destination: destination, + isPrimaryComponent: isPrimaryComponent, + components: components, + category: category, + quantity: quantity, + mergeKey: mergeKey, + quantifiedDetailContext: quantifiedDetailContext, + quantifiedDetailSecondaryContext: quantifiedDetailSecondaryContext, + quantityScale: quantityScale + ) + } } - init(systemName: String, loads: [SavedLoad], unitSystem: UnitSystem) { + init( + systemName: String, + loads: [SavedLoad], + batteries: [SavedBattery], + chargers: [SavedCharger], + unitSystem: UnitSystem + ) { self.systemName = systemName self.loads = loads + self.batteries = batteries + self.chargers = chargers self.unitSystem = unitSystem - let initialKeys = loads.flatMap { load in - load.bomCompletedItemIDs.map { SystemBillOfMaterialsView.storageKey(for: load, itemID: $0) } + let loadKeys = loads.flatMap { load in + load.bomCompletedItemIDs.map { SystemBillOfMaterialsView.storageKey(for: .load(load), itemID: $0) } } + let batteryKeys = batteries.flatMap { battery in + battery.bomCompletedItemIDs.map { SystemBillOfMaterialsView.storageKey(for: .battery(battery), itemID: $0) } + } + let chargerKeys = chargers.flatMap { charger in + charger.bomCompletedItemIDs.map { SystemBillOfMaterialsView.storageKey(for: .charger(charger), itemID: $0) } + } + let initialKeys = loadKeys + batteryKeys + chargerKeys _completedItemIDs = State(initialValue: Set(initialKeys)) _suppressRowTapForID = State(initialValue: nil) } @@ -48,92 +262,23 @@ struct SystemBillOfMaterialsView: View { var body: some View { NavigationStack { List { - if sortedLoads.isEmpty { + if categorySections.isEmpty { Section("Components") { - Text("No loads saved in this system yet.") + Text( + String( + localized: "bom.empty.message", + comment: "Placeholder shown when there are no components to list in the BOM" + ) + ) .font(.footnote) .foregroundColor(.secondary) } - } else { - ForEach(sortedLoads) { load in - Section(header: sectionHeader(for: load)) { - ForEach(items(for: load)) { item in - let isCompleted = completedItemIDs.contains(item.id) - let destinationURL = destinationURL(for: item.destination, load: load) + } - HStack(spacing: 12) { - let accessibilityLabel: String = { - if isCompleted { - let format = NSLocalizedString( - "bom.accessibility.mark.incomplete", - comment: "Accessibility label instructing VoiceOver to mark an item incomplete" - ) - return String.localizedStringWithFormat(format, item.title) - } else { - let format = NSLocalizedString( - "bom.accessibility.mark.complete", - comment: "Accessibility label instructing VoiceOver to mark an item complete" - ) - return String.localizedStringWithFormat(format, item.title) - } - }() - - Image(systemName: isCompleted ? "checkmark.circle.fill" : "circle") - .foregroundColor(isCompleted ? .accentColor : .secondary) - .imageScale(.large) - .onTapGesture { - setCompletion(!isCompleted, for: load, item: item) - suppressRowTapForID = item.id - } - .accessibilityLabel(accessibilityLabel) - - VStack(alignment: .leading, spacing: 4) { - Text(item.title) - .font(.headline) - .foregroundStyle(isCompleted ? Color.primary.opacity(0.7) : Color.primary) - .strikethrough(isCompleted, color: .accentColor.opacity(0.6)) - - if item.isPrimaryComponent { - Text(String(localized: "component.fallback.name", comment: "Tag label marking an item as the component itself")) - .font(.caption2.weight(.medium)) - .foregroundColor(.accentColor) - .padding(.horizontal, 6) - .padding(.vertical, 2) - .background(Color.accentColor.opacity(0.15), in: Capsule()) - } - - Text(item.detail) - .font(.subheadline) - .foregroundColor(.secondary) - } - - Spacer(minLength: 8) - - if destinationURL != nil { - Image(systemName: "arrow.up.right") - .font(.footnote.weight(.semibold)) - .foregroundColor(.secondary) - } - } - .padding(.vertical, 10) - .contentShape(Rectangle()) - .listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 16)) - .listRowBackground( - Color(.secondarySystemGroupedBackground) - ) - .onTapGesture { - if suppressRowTapForID == item.id { - suppressRowTapForID = nil - return - } - if let destinationURL { - openURL(destinationURL) - } - setCompletion(true, for: load, item: item) - suppressRowTapForID = nil - suppressRowTapForID = nil - } - } + ForEach(categorySections) { section in + Section(header: sectionHeader(title: section.title, subtitle: section.subtitle)) { + ForEach(section.items) { item in + itemRow(for: item) } } } @@ -162,6 +307,26 @@ struct SystemBillOfMaterialsView: View { Button("Close") { dismiss() } + .accessibilityIdentifier("system-bom-close-button") + + } + ToolbarItem(placement: .primaryAction) { + Button(action: exportBillOfMaterialsPDF) { + if isExportingPDF { + ProgressView() + .progressViewStyle(.circular) + } else { + Label( + NSLocalizedString( + "bom.export.pdf.button", + comment: "Button title for exporting the BOM as a PDF document" + ), + systemImage: "square.and.arrow.up" + ) + } + } + .disabled(categorySections.isEmpty || isExportingPDF) + .accessibilityIdentifier("system-bom-export-button") } } .onAppear { @@ -170,6 +335,206 @@ struct SystemBillOfMaterialsView: View { } } .accessibilityIdentifier("system-bom-view") + .sheet(item: $activeShareItem, onDismiss: cleanupShareItem) { item in + ShareSheet(items: [item.url]) + } + .alert(item: $exportError) { error in + Alert( + title: Text( + NSLocalizedString( + "bom.export.pdf.error.title", + comment: "Title for the alert shown when a PDF export fails" + ) + ), + message: Text(error.message), + dismissButton: .default( + Text( + NSLocalizedString( + "generic.ok", + comment: "Default acknowledgement button title" + ) + ) + ) + ) + } + } + + private func exportBillOfMaterialsPDF() { + if categorySections.isEmpty { + exportError = ExportError( + message: NSLocalizedString( + "bom.export.pdf.error.empty", + comment: "Error message shown when attempting to export a PDF without any components" + ) + ) + return + } + + isExportingPDF = true + let exporter = SystemBillOfMaterialsPDFExporter() + let sections = sectionSnapshots + let currentSystemName = systemName + let currentUnitSystem = unitSystem + + Task { + do { + let url = try exporter.export( + systemName: currentSystemName, + unitSystem: currentUnitSystem, + sections: sections + ) + await MainActor.run { + activeShareItem = ExportedPDFShareItem(url: url) + } + } catch { + await MainActor.run { + exportError = ExportError(message: error.localizedDescription) + } + } + + await MainActor.run { + isExportingPDF = false + } + } + } + + private func cleanupShareItem() { + if let url = activeShareItem?.url { + try? FileManager.default.removeItem(at: url) + } + activeShareItem = nil + } + + @ViewBuilder + private func itemRow(for item: Item) -> some View { + let isCompleted = item.storageKeys.allSatisfy { completedItemIDs.contains($0) } + let destinationURL = destinationURL(for: item.destination, component: item.primaryComponent) + + HStack(spacing: 12) { + let accessibilityLabel: String = { + let formatKey = isCompleted ? "bom.accessibility.mark.incomplete" : "bom.accessibility.mark.complete" + let format = NSLocalizedString( + formatKey, + comment: "Accessibility label instructing VoiceOver to toggle a BOM item" + ) + return String.localizedStringWithFormat(format, item.title) + }() + + Image(systemName: isCompleted ? "checkmark.circle.fill" : "circle") + .foregroundColor(isCompleted ? .accentColor : .secondary) + .imageScale(.large) + .onTapGesture { + setCompletion(!isCompleted, for: item) + suppressRowTapForID = item.id + } + .accessibilityLabel(accessibilityLabel) + + VStack(alignment: .leading, spacing: 4) { + Text(item.title) + .font(.headline) + .foregroundStyle(isCompleted ? Color.primary.opacity(0.7) : Color.primary) + .strikethrough(isCompleted, color: .accentColor.opacity(0.6)) + + if let metric = item.primaryMetricText { + Text(metric) + .font(.title3.weight(.semibold)) + .foregroundColor(.accentColor) + } + + if item.isPrimaryComponent { + Text(String(localized: "component.fallback.name", comment: "Tag label marking an item as the component itself")) + .font(.caption2.weight(.medium)) + .foregroundColor(.accentColor) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(Color.accentColor.opacity(0.15), in: Capsule()) + } + + if shouldShowDetail(for: item) { + Text(item.detail) + .font(.subheadline) + .foregroundColor(.secondary) + } + } + + Spacer(minLength: 8) + + if destinationURL != nil { + Image(systemName: "arrow.up.right") + .font(.footnote.weight(.semibold)) + .foregroundColor(.secondary) + } + } + .padding(.vertical, 10) + .contentShape(Rectangle()) + .listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 16)) + .listRowBackground( + Color(.secondarySystemGroupedBackground) + ) + .onTapGesture { + if suppressRowTapForID == item.id { + suppressRowTapForID = nil + return + } + if let destinationURL { + openURL(destinationURL) + } + setCompletion(true, for: item) + suppressRowTapForID = nil + } + } + + private func shouldShowDetail(for item: Item) -> Bool { + !item.detail.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + } + + private var categorySections: [CategorySection] { + var allItems: [Item] = [] + sortedLoads.forEach { allItems.append(contentsOf: loadItems(for: $0)) } + sortedBatteries.forEach { allItems.append(contentsOf: batteryItems(for: $0)) } + sortedChargers.forEach { allItems.append(contentsOf: chargerItems(for: $0)) } + + let consolidated = consolidateSimilarItems(allItems) + let grouped = Dictionary(grouping: consolidated, by: \Item.category) + + return Category.ordered.compactMap { category in + guard let items = grouped[category], !items.isEmpty else { return nil } + return CategorySection(category: category, items: items) + } + } + + private var sectionSnapshots: [BillOfMaterialsSectionSnapshot] { + categorySections.map { $0.snapshot } + } + + private func consolidateSimilarItems(_ items: [Item]) -> [Item] { + var results: [Item] = [] + var indexByKey: [String: Int] = [:] + + for item in items { + guard let key = item.mergeKey else { + results.append(item) + continue + } + + if let index = indexByKey[key] { + var existing = results[index] + let combinedComponents = existing.components + item.components + let combinedStorageKeys = existing.storageKeys + item.storageKeys + let newQuantity = existing.quantity + item.quantity + var updated = existing.updatingQuantity(newQuantity, components: combinedComponents, storageKeys: combinedStorageKeys) + if existing.id != key { + updated = updated.replacingID(key) + } + results[index] = updated + } else { + indexByKey[key] = results.count + let base = item.updatingQuantity(item.quantity, components: item.components, storageKeys: item.storageKeys) + results.append(base.replacingID(key)) + } + } + + return results } private var sortedLoads: [SavedLoad] { @@ -178,18 +543,41 @@ struct SystemBillOfMaterialsView: View { } } - private func sectionHeader(for load: SavedLoad) -> some View { + private var sortedBatteries: [SavedBattery] { + batteries.sorted { lhs, rhs in + lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending + } + } + + private var sortedChargers: [SavedCharger] { + chargers.sorted { lhs, rhs in + lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending + } + } + + private func sectionHeader(title: String, subtitle: String) -> some View { VStack(alignment: .leading, spacing: 2) { - let fallbackTitle = String(localized: "component.fallback.name", comment: "Fallback title for a component that lacks a name") - Text(load.name.isEmpty ? fallbackTitle : load.name) + Text(title) .font(.headline) - Text(dateFormatter.string(from: load.timestamp)) + Text(subtitle) .font(.caption) .foregroundColor(.secondary) } } - private func items(for load: SavedLoad) -> [Item] { + private func items(for component: Component) -> [Item] { + switch component { + case .load(let load): + return loadItems(for: load) + case .battery(let battery): + return batteryItems(for: battery) + case .charger(let charger): + return chargerItems(for: charger) + } + } + + private func loadItems(for load: SavedLoad) -> [Item] { + let component: Component = .load(load) let lengthValue: Double if unitSystem == .imperial { lengthValue = load.length * 3.28084 @@ -221,8 +609,6 @@ struct SystemBillOfMaterialsView: View { } } - let cableDetail = "\(lengthLabel) • \(crossSectionLabel)" - let calculatedPower = load.power > 0 ? load.power : load.voltage * load.current let powerDetail = String(format: "%.0f W @ %.1f V", calculatedPower, load.voltage) @@ -233,12 +619,15 @@ struct SystemBillOfMaterialsView: View { ) let fuseDetail = String.localizedStringWithFormat(fuseDetailFormat, fuseRating) + let terminalCount = 4 let cableShoesDetailFormat = NSLocalizedString( "bom.terminals.detail", comment: "Description for the cable terminals item in the BOM list" ) - let cableShoesDetail = String.localizedStringWithFormat(cableShoesDetailFormat, crossSectionLabel.lowercased()) - + let cableShoesDetail = String.localizedStringWithFormat( + cableShoesDetailFormat, + crossSectionLabel.lowercased() + ) let affiliateURL = load.affiliateURLString.flatMap { URL(string: $0) } let deviceQuery = load.name.isEmpty ? String(format: "DC device %.0fW %.0fV", calculatedPower, load.voltage) @@ -247,107 +636,266 @@ struct SystemBillOfMaterialsView: View { let redCableQuery = "\(gaugeQuery) red battery cable" let blackCableQuery = "\(gaugeQuery) black battery cable" let fuseQuery = "inline fuse holder \(fuseRating)A" - let terminalQuery = "\(gaugeQuery) cable shoes" + let terminalQuery = "\(gaugeQuery) cable shoes pack of \(terminalCount)" + + let componentStorageKey = Self.storageKey(for: component, itemID: "component") + let redCableStorageKey = Self.storageKey(for: component, itemID: "cable-red") + let blackCableStorageKey = Self.storageKey(for: component, itemID: "cable-black") + let fuseStorageKey = Self.storageKey(for: component, itemID: "fuse") + let terminalsStorageKey = Self.storageKey(for: component, itemID: "terminals") + + let lengthQuantity = Int((max(lengthValue, 0) * 100).rounded()) + let redCableMergeKey = "cable.red::\(crossSectionLabel)::\(unitSystem.lengthUnit)" + let blackCableMergeKey = "cable.black::\(crossSectionLabel)::\(unitSystem.lengthUnit)" let items: [Item] = [ Item( - id: Self.storageKey(for: load, itemID: "component"), + id: componentStorageKey, + storageKeys: [componentStorageKey], logicalID: "component", title: load.name.isEmpty ? String(localized: "component.fallback.name", comment: "Fallback name for a component when no name is provided") : load.name, detail: powerDetail, iconSystemName: "bolt.fill", destination: affiliateURL.map { .affiliate($0) } ?? .amazonSearch(deviceQuery), - isPrimaryComponent: true + isPrimaryComponent: true, + components: [component], + category: .components, + quantity: 1, + mergeKey: nil, + quantifiedDetailContext: nil, + quantifiedDetailSecondaryContext: nil, + quantityScale: nil ), Item( - id: Self.storageKey(for: load, itemID: "cable-red"), + id: redCableStorageKey, + storageKeys: [redCableStorageKey], logicalID: "cable-red", title: String(localized: "bom.item.cable.red", comment: "Title for the red power cable item"), - detail: cableDetail, + detail: "", iconSystemName: "bolt.horizontal.circle", destination: .amazonSearch(redCableQuery), - isPrimaryComponent: false + isPrimaryComponent: false, + components: [component], + category: .cables, + quantity: lengthQuantity, + mergeKey: redCableMergeKey, + quantifiedDetailContext: unitSystem.lengthUnit, + quantifiedDetailSecondaryContext: crossSectionLabel, + quantityScale: 100.0 ), Item( - id: Self.storageKey(for: load, itemID: "cable-black"), + id: blackCableStorageKey, + storageKeys: [blackCableStorageKey], logicalID: "cable-black", title: String(localized: "bom.item.cable.black", comment: "Title for the black power cable item"), - detail: cableDetail, + detail: "", iconSystemName: "bolt.horizontal.circle", destination: .amazonSearch(blackCableQuery), - isPrimaryComponent: false + isPrimaryComponent: false, + components: [component], + category: .cables, + quantity: lengthQuantity, + mergeKey: blackCableMergeKey, + quantifiedDetailContext: unitSystem.lengthUnit, + quantifiedDetailSecondaryContext: crossSectionLabel, + quantityScale: 100.0 ), Item( - id: Self.storageKey(for: load, itemID: "fuse"), + id: fuseStorageKey, + storageKeys: [fuseStorageKey], logicalID: "fuse", title: String(localized: "bom.item.fuse", comment: "Title for the fuse and holder item"), detail: fuseDetail, iconSystemName: "bolt.shield", destination: .amazonSearch(fuseQuery), - isPrimaryComponent: false + isPrimaryComponent: false, + components: [component], + category: .fuses, + quantity: 1, + mergeKey: nil, + quantifiedDetailContext: nil, + quantifiedDetailSecondaryContext: nil, + quantityScale: nil ), Item( - id: Self.storageKey(for: load, itemID: "terminals"), + id: terminalsStorageKey, + storageKeys: [terminalsStorageKey], logicalID: "terminals", title: String(localized: "bom.item.terminals", comment: "Title for the cable terminals item"), detail: cableShoesDetail, iconSystemName: "wrench.and.screwdriver", destination: .amazonSearch(terminalQuery), - isPrimaryComponent: false + isPrimaryComponent: false, + components: [component], + category: .accessories, + quantity: terminalCount, + mergeKey: "terminals::\(crossSectionLabel.lowercased())", + quantifiedDetailContext: nil, + quantifiedDetailSecondaryContext: nil, + quantityScale: nil ) ] return items } - private func destinationURL(for destination: Item.Destination, load: SavedLoad) -> URL? { + private func batteryItems(for battery: SavedBattery) -> [Item] { + let component: Component = .battery(battery) + let usableCapacityLabel = NSLocalizedString( + "battery.bank.metric.usable_capacity", + comment: "Label describing usable battery capacity" + ) + let capacityLabel = String(format: "%.0f Ah @ %.1f V", battery.capacityAmpHours, battery.nominalVoltage) + let usableLabel = String(format: "%@: %.0f Ah", usableCapacityLabel, battery.usableCapacityAmpHours) + let detail = [capacityLabel, battery.chemistry.displayName, usableLabel].joined(separator: " • ") + let capacityQuery = max(1, Int(round(battery.capacityAmpHours))) + let voltageQuery = max(1, Int(round(battery.nominalVoltage))) + let query = "\(capacityQuery)Ah \(voltageQuery)V \(battery.chemistry.displayName) battery" + let storageKey = Self.storageKey(for: component, itemID: "battery") + + return [ + Item( + id: storageKey, + storageKeys: [storageKey], + logicalID: "battery", + title: battery.name.isEmpty ? String(localized: "component.fallback.name", comment: "Fallback name for a component when no name is provided") : battery.name, + detail: detail, + iconSystemName: battery.iconName, + destination: .amazonSearch(query), + isPrimaryComponent: true, + components: [component], + category: .batteries, + quantity: 1, + mergeKey: nil, + quantifiedDetailContext: nil, + quantifiedDetailSecondaryContext: nil, + quantityScale: nil + ) + ] + } + + private func chargerItems(for charger: SavedCharger) -> [Item] { + let component: Component = .charger(charger) + let effectivePower = charger.effectivePowerWatts + let powerString = String(format: "%.0f W", effectivePower) + let outputString = String(format: "%.0f A @ %.1f V", charger.maxCurrentAmps, charger.outputVoltage) + let inputString = String(format: "%.0f V AC", charger.inputVoltage) + let detail = [powerString, outputString, inputString].joined(separator: " • ") + let voltageQuery = max(1, Int(round(charger.outputVoltage))) + let currentQuery = max(1, Int(round(charger.maxCurrentAmps))) + let query = "\(voltageQuery)V \(currentQuery)A battery charger" + let affiliateURL = charger.affiliateURLString.flatMap { URL(string: $0) } + let storageKey = Self.storageKey(for: component, itemID: "charger") + + return [ + Item( + id: storageKey, + storageKeys: [storageKey], + logicalID: "charger", + title: charger.name.isEmpty ? String(localized: "component.fallback.name", comment: "Fallback name for a component when no name is provided") : charger.name, + detail: detail, + iconSystemName: charger.iconName, + destination: affiliateURL.map { .affiliate($0) } ?? .amazonSearch(query), + isPrimaryComponent: true, + components: [component], + category: .components, + quantity: 1, + mergeKey: nil, + quantifiedDetailContext: nil, + quantifiedDetailSecondaryContext: nil, + quantityScale: nil + ) + ] + } + + private func destinationURL(for destination: Item.Destination, component: Component) -> URL? { switch destination { case .affiliate(let url): return url case .amazonSearch(let query): - let countryCode = load.affiliateCountryCode ?? Locale.current.region?.identifier + let countryCode = affiliateCountryCode(for: component) ?? Locale.current.region?.identifier return AmazonAffiliate.searchURL(query: query, countryCode: countryCode) } } - private static func storageKey(for load: SavedLoad, itemID: String) -> String { - if load.identifier.isEmpty { - load.identifier = UUID().uuidString + private func affiliateCountryCode(for component: Component) -> String? { + switch component { + case .load(let load): + return load.affiliateCountryCode + case .battery: + return nil + case .charger(let charger): + return charger.affiliateCountryCode } - return "\(load.identifier)::\(itemID)" } - private func setCompletion(_ isCompleted: Bool, for load: SavedLoad, item: Item) { + private static func storageKey(for component: Component, itemID: String) -> String { + switch component { + case .load(let load): + if load.identifier.isEmpty { + load.identifier = UUID().uuidString + } + return "\(load.identifier)::\(itemID)" + case .battery(let battery): + return "\(battery.id.uuidString)::\(itemID)" + case .charger(let charger): + if charger.identifier.isEmpty { + charger.identifier = UUID().uuidString + } + return "\(charger.identifier)::\(itemID)" + } + } + + private func setCompletion(_ isCompleted: Bool, for item: Item) { if isCompleted { - completedItemIDs.insert(item.id) + item.storageKeys.forEach { completedItemIDs.insert($0) } } else { - completedItemIDs.remove(item.id) + item.storageKeys.forEach { completedItemIDs.remove($0) } } - if load.identifier.isEmpty { - load.identifier = UUID().uuidString + for component in item.components { + switch component { + case .load(let load): + if load.identifier.isEmpty { + load.identifier = UUID().uuidString + } + updateCompletionList(isCompleted, logicalID: item.logicalID, list: &load.bomCompletedItemIDs) + case .battery(let battery): + updateCompletionList(isCompleted, logicalID: item.logicalID, list: &battery.bomCompletedItemIDs) + case .charger(let charger): + if charger.identifier.isEmpty { + charger.identifier = UUID().uuidString + } + updateCompletionList(isCompleted, logicalID: item.logicalID, list: &charger.bomCompletedItemIDs) + } } + } - var stored = Set(load.bomCompletedItemIDs) + private func updateCompletionList(_ isCompleted: Bool, logicalID: String, list: inout [String]) { + var stored = Set(list) if isCompleted { - stored.insert(item.logicalID) + stored.insert(logicalID) } else { - stored.remove(item.logicalID) + stored.remove(logicalID) } - load.bomCompletedItemIDs = Array(stored).sorted() + list = Array(stored).sorted() } private func refreshCompletedItems() { - let keys = loads.flatMap { load in - load.bomCompletedItemIDs.map { Self.storageKey(for: load, itemID: $0) } + let loadKeys = loads.flatMap { load in + load.bomCompletedItemIDs.map { Self.storageKey(for: .load(load), itemID: $0) } } - completedItemIDs = Set(keys) + let batteryKeys = batteries.flatMap { battery in + battery.bomCompletedItemIDs.map { Self.storageKey(for: .battery(battery), itemID: $0) } + } + let chargerKeys = chargers.flatMap { charger in + charger.bomCompletedItemIDs.map { Self.storageKey(for: .charger(charger), itemID: $0) } + } + completedItemIDs = Set(loadKeys + batteryKeys + chargerKeys) } private func recommendedFuse(for load: SavedLoad) -> Int { - let targetFuse = load.current * 1.25 - let standardFuses = [1, 2, 3, 5, 7, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500, 600, 700, 800] - return standardFuses.first { $0 >= Int(targetFuse.rounded(.up)) } ?? standardFuses.last ?? 0 + ElectricalCalculations.recommendedFuse(forCurrent: load.current) } private func awgFromCrossSection(_ crossSectionMM2: Double) -> Double { @@ -380,3 +928,81 @@ struct SystemBillOfMaterialsView: View { return formatter } } + +#Preview("System BOM – Metric") { + // Sample SavedLoad model stubs for preview + let load1 = SavedLoad( + name: "Fridge", + voltage: 12.0, + current: 6.0, + power: 72.0, + length: 6.5, + crossSection: 0.0, + iconName: "", + colorName: "", + isWattMode: true, + dutyCyclePercent: 19, + dailyUsageHours: 0, + system: nil, + remoteIconURLString: nil, + affiliateURLString: nil, + affiliateCountryCode: nil, + bomCompletedItemIDs: [], + identifier: UUID().uuidString + ) + + let battery = SavedBattery( + name: "House Bank", + nominalVoltage: 12.8, + capacityAmpHours: 200, + chemistry: .lithiumIronPhosphate, + system: nil + ) + + let charger = SavedCharger( + name: "Shore Charger", + inputVoltage: 230, + outputVoltage: 14.6, + maxCurrentAmps: 40, + maxPowerWatts: 600, + system: nil + ) + + SystemBillOfMaterialsView( + systemName: "Camper Van", + loads: [load1], + batteries: [battery], + chargers: [charger], + unitSystem: .metric + ) +} + +#Preview("System BOM – Imperial") { + let load = SavedLoad( + name: "Inverter", + voltage: 12.0, + current: 40.0, + power: 480.0, + length: 12.0, + crossSection: 21.2, + iconName: "", + colorName: "", + isWattMode: false, + dutyCyclePercent: 100, + dailyUsageHours: 0, + system: nil, + remoteIconURLString: nil, + affiliateURLString: nil, + affiliateCountryCode: nil, + bomCompletedItemIDs: ["component", "cable-red"], + identifier: UUID().uuidString + ) + + SystemBillOfMaterialsView( + systemName: "Boat", + loads: [load], + batteries: [], + chargers: [], + unitSystem: .imperial + ) +} diff --git a/Cable/Systems/SystemsOnboardingView.swift b/Cable/Systems/SystemsOnboardingView.swift index 6cd83c1..7caa23a 100644 --- a/Cable/Systems/SystemsOnboardingView.swift +++ b/Cable/Systems/SystemsOnboardingView.swift @@ -1,5 +1,4 @@ import SwiftUI -import PostHog struct SystemsOnboardingView: View { @State private var systemName: String = String(localized: "default.system.name", comment: "Default placeholder name for a system") @@ -94,7 +93,7 @@ struct SystemsOnboardingView: View { .onAppear(perform: resetState) .onReceive(timer) { _ in advanceCarousel() } .task { - PostHogSDK.shared.capture("Launched") + AnalyticsTracker.log("Launched") } } @@ -106,7 +105,7 @@ struct SystemsOnboardingView: View { private func createSystem() { isFieldFocused = false let trimmed = systemName.trimmingCharacters(in: .whitespacesAndNewlines) - PostHogSDK.shared.capture("System Created", properties: ["name": trimmed]) + AnalyticsTracker.log("System Created", properties: ["name": trimmed]) guard !trimmed.isEmpty else { return } onCreate(trimmed) } diff --git a/Cable/Systems/SystemsView.swift b/Cable/Systems/SystemsView.swift index 94c68c7..4e2a58c 100644 --- a/Cable/Systems/SystemsView.swift +++ b/Cable/Systems/SystemsView.swift @@ -8,7 +8,6 @@ import SwiftUI import SwiftData -import PostHog struct SystemsView: View { @Environment(\.modelContext) private var modelContext @@ -77,48 +76,16 @@ struct SystemsView: View { } else { List { ForEach(systems) { system in - NavigationLink(destination: LoadsView(system: system)) { - HStack(spacing: 12) { - ZStack { - RoundedRectangle(cornerRadius: 10) - .fill(Color.componentColor(named: system.colorName)) - .frame(width: 44, height: 44) - - Image(systemName: system.iconName) - .font(.title3) - .foregroundColor(.white) - } - - VStack(alignment: .leading, spacing: 4) { - Text(system.name) - .fontWeight(.medium) - - if !system.location.isEmpty { - Text(system.location) - .font(.caption) - .foregroundColor(.secondary) + Button { + handleSystemSelection(system) + } label: { + systemRow(for: system) + .contentShape(Rectangle()) } - - Text(componentSummary(for: system)) - .font(.caption) - .foregroundColor(.secondary) - } - - Spacer() - } - .padding(.vertical, 4) - } - .simultaneousGesture( - TapGesture().onEnded { - PostHogSDK.shared.capture( - "System Opened", - properties: [ - "name": system.name, - "source": "list" - ] - ) - } - ) + .buttonStyle(.plain) + .accessibilityLabel(system.name) + .accessibilityHint(Text("systems.list.row.accessibility.hint", comment: "Accessibility hint for systems list row")) + .accessibilityAddTraits(.isButton) } .onDelete(perform: deleteSystems) } @@ -137,7 +104,7 @@ struct SystemsView: View { ToolbarItem(placement: .navigationBarTrailing) { HStack { Button(action: { - PostHogSDK.shared.capture("System Create Navigation") + AnalyticsTracker.log("System Create Navigation") createNewSystem() }) { Image(systemName: "plus") @@ -175,13 +142,67 @@ struct SystemsView: View { } private func openSettings() { - PostHogSDK.shared.capture("Settings Opened") + AnalyticsTracker.log("Settings Opened") showingSettings = true } + private func handleSystemSelection(_ system: ElectricalSystem) { + AnalyticsTracker.log( + "System Opened", + properties: [ + "name": system.name, + "source": "list" + ] + ) + navigateToSystem( + system, + presentSystemEditor: false, + loadToOpen: nil, + source: "list" + ) + } + + @ViewBuilder + private func systemRow(for system: ElectricalSystem) -> some View { + HStack(spacing: 12) { + ZStack { + RoundedRectangle(cornerRadius: 10) + .fill(Color.componentColor(named: system.colorName)) + .frame(width: 44, height: 44) + + Image(systemName: system.iconName) + .font(.title3) + .foregroundColor(.white) + } + + VStack(alignment: .leading, spacing: 4) { + Text(system.name) + .fontWeight(.medium) + + if !system.location.isEmpty { + Text(system.location) + .font(.caption) + .foregroundColor(.secondary) + } + + Text(componentSummary(for: system)) + .font(.caption) + .foregroundColor(.secondary) + } + + Spacer() + + Image(systemName: "chevron.right") + .font(.footnote.weight(.semibold)) + .foregroundColor(.secondary.opacity(0.6)) + } + .padding(.vertical, 4) + .frame(maxWidth: .infinity, alignment: .leading) + } + private func createNewSystem() { let system = makeSystem() - PostHogSDK.shared.capture( + AnalyticsTracker.log( "System Created", properties: [ "name": system.name, @@ -198,7 +219,7 @@ struct SystemsView: View { private func createNewSystem(named name: String) { let system = makeSystem(preferredName: name) - PostHogSDK.shared.capture( + AnalyticsTracker.log( "System Created", properties: [ "name": system.name, @@ -233,7 +254,7 @@ struct SystemsView: View { animated: Bool = true, source: String = "programmatic" ) { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "System Opened", properties: [ "name": system.name, @@ -300,7 +321,7 @@ struct SystemsView: View { private func addComponentFromLibrary(_ item: ComponentLibraryItem) { let system = makeSystem() - PostHogSDK.shared.capture( + AnalyticsTracker.log( "System Created", properties: [ "name": system.name, @@ -308,7 +329,7 @@ struct SystemsView: View { ] ) let load = createLoad(from: item, in: system) - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Library Load Added", properties: [ "id": item.id, @@ -397,7 +418,7 @@ struct SystemsView: View { let systemsToDelete = offsets.map { systems[$0] } withAnimation { for system in systemsToDelete { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "System Deleted", properties: [ "name": system.name, @@ -414,7 +435,7 @@ struct SystemsView: View { let descriptor = FetchDescriptor() if let loads = try? modelContext.fetch(descriptor) { for load in loads where load.system == system { - PostHogSDK.shared.capture( + AnalyticsTracker.log( "Load Deleted", properties: [ "name": load.name, diff --git a/Cable/UITestSampleData.swift b/Cable/UITestSampleData.swift index dd9ba32..cfab558 100644 --- a/Cable/UITestSampleData.swift +++ b/Cable/UITestSampleData.swift @@ -7,27 +7,44 @@ import Foundation import SwiftData enum UITestSampleData { - static let argument = "--uitest-sample-data" + static let sampleArgument = "--uitest-sample-data" + static let resetArgument = "--uitest-reset-data" - static func prepareIfNeeded(container: ModelContainer) { + static func handleLaunchArguments(container: ModelContainer) { #if DEBUG - guard ProcessInfo.processInfo.arguments.contains(argument) else { return } + let arguments = ProcessInfo.processInfo.arguments + NSLog("UITestSampleData arguments: %@", arguments.joined(separator: ", ")) + guard arguments.contains(sampleArgument) || arguments.contains(resetArgument) else { return } let context = ModelContext(container) do { - try clearExistingData(in: context) - try seedSampleData(in: context) - try context.save() + if arguments.contains(resetArgument) { + NSLog("UITestSampleData resetting data store") + try clearExistingData(in: context) + } + + if arguments.contains(sampleArgument) { + NSLog("UITestSampleData seeding sample data") + if !arguments.contains(resetArgument) { + try clearExistingData(in: context) + } + try seedSampleData(in: context) + } + + if context.hasChanges { + try context.save() + NSLog("UITestSampleData save completed") + } } catch { - assertionFailure("Failed to seed UI test sample data: \(error)") + assertionFailure("Failed to prepare UI test data: \(error)") } #endif } } #if DEBUG -private extension UITestSampleData { +extension UITestSampleData { static func clearExistingData(in context: ModelContext) throws { let systemDescriptor = FetchDescriptor() let loadDescriptor = FetchDescriptor() diff --git a/Cable/de.lproj/Localizable.strings b/Cable/de.lproj/Localizable.strings index 9234361..40b1e3e 100644 --- a/Cable/de.lproj/Localizable.strings +++ b/Cable/de.lproj/Localizable.strings @@ -144,6 +144,29 @@ "bom.navigation.title.system" = "Stückliste – %@"; "bom.size.unknown" = "Größe offen"; "bom.terminals.detail" = "Ring- oder Gabelkabelschuhe für %@-Leitungen"; +"bom.empty.message" = "Dieses System hat noch keine Komponenten."; +"bom.export.pdf.button" = "PDF exportieren"; +"bom.export.pdf.error.title" = "Export fehlgeschlagen"; +"bom.export.pdf.error.empty" = "Füge vor dem Export mindestens eine Komponente hinzu."; +"bom.pdf.header.title" = "System-Stückliste"; +"bom.pdf.header.subtitle" = "%@ • %@"; +"bom.pdf.header.inline" = "Einheitensystem: %@"; +"bom.pdf.placeholder.empty" = "Keine Komponenten verfügbar."; +"bom.pdf.page.number" = "Seite %d"; +"bom.category.components.title" = "Komponenten & Ladegeräte"; +"bom.category.components.subtitle" = "Hauptverbraucher, Regler und Ladehardware."; +"bom.category.batteries.title" = "Batterien"; +"bom.category.batteries.subtitle" = "Hausspeicher und Batteriebänke."; +"bom.category.cables.title" = "Kabel"; +"bom.category.cables.subtitle" = "Passende Leitungen für jede Strecke."; +"bom.category.fuses.title" = "Sicherungen"; +"bom.category.fuses.subtitle" = "Stromkreisschutz und Halter."; +"bom.category.accessories.title" = "Zubehör"; +"bom.category.accessories.subtitle" = "Sicherungen, Kabelschuhe und weiteres Montagematerial."; +"bom.cable.detail.quantified" = "%1$dx %2$@"; +"bom.quantity.count.badge" = "%d×"; +"bom.quantity.length.badge" = "%1$.1f %2$@"; +"bom.quantity.length.badge.with.spec" = "%1$.1f %2$@ · %3$@"; "cable.pro.privacy.label" = "Datenschutz"; "cable.pro.privacy.url" = "https://voltplan.app/de/datenschutz"; "cable.pro.terms.label" = "Nutzungsbedingungen"; @@ -264,7 +287,7 @@ "sample.load.charger.name" = "Werkzeugladegerät"; "sample.load.compressor.name" = "Luftkompressor"; "sample.load.fridge.name" = "Kompressor-Kühlschrank"; -"sample.load.lighting.name" = "LED-Streifenbeleuchtung"; +"sample.load.lighting.name" = "LED-Streifen"; "sample.system.rv.location" = "12V Wohnstromkreis"; "sample.system.rv.name" = "Abenteuer-Van"; "sample.system.workshop.location" = "Werkzeugecke"; @@ -337,6 +360,7 @@ "cable.pro.alert.restored.body" = "Deine bisherigen Käufe sind wieder verfügbar."; "cable.pro.alert.error.title" = "Kauf fehlgeschlagen"; "cable.pro.alert.error.generic" = "Etwas ist schiefgelaufen. Bitte versuche es erneut."; +"generic.ok" = "OK"; "cable.pro.trial.badge" = "Enthält eine %@ Testphase"; "cable.pro.subscription.renews" = "Verlängert sich %@."; "cable.pro.subscription.trialThenRenews" = "Testphase, danach Verlängerung %@."; diff --git a/Cable/es.lproj/Localizable.strings b/Cable/es.lproj/Localizable.strings index 88a199f..8074d1d 100644 --- a/Cable/es.lproj/Localizable.strings +++ b/Cable/es.lproj/Localizable.strings @@ -14,6 +14,29 @@ "bom.navigation.title.system" = "Lista de materiales – %@"; "bom.size.unknown" = "Tamaño por definir"; "bom.terminals.detail" = "Terminales de anillo o de horquilla para cables de %@"; +"bom.empty.message" = "Todavía no hay componentes guardados en este sistema."; +"bom.export.pdf.button" = "Exportar PDF"; +"bom.export.pdf.error.title" = "Exportación fallida"; +"bom.export.pdf.error.empty" = "Agrega al menos un componente antes de exportar."; +"bom.pdf.header.title" = "Lista de materiales del sistema"; +"bom.pdf.header.subtitle" = "%@ • %@"; +"bom.pdf.header.inline" = "Sistema de unidades: %@"; +"bom.pdf.placeholder.empty" = "No hay componentes disponibles."; +"bom.pdf.page.number" = "Página %d"; +"bom.category.components.title" = "Componentes y cargadores"; +"bom.category.components.subtitle" = "Dispositivos principales, controladores y equipos de carga."; +"bom.category.batteries.title" = "Baterías"; +"bom.category.batteries.subtitle" = "Bancos domésticos y almacenamiento."; +"bom.category.cables.title" = "Cables"; +"bom.category.cables.subtitle" = "Tendidos dimensionados para cada circuito."; +"bom.category.fuses.title" = "Fusibles"; +"bom.category.fuses.subtitle" = "Protección de circuitos y portafusibles."; +"bom.category.accessories.title" = "Accesorios"; +"bom.category.accessories.subtitle" = "Fusibles, terminales y piezas de soporte."; +"bom.cable.detail.quantified" = "%1$dx %2$@"; +"bom.quantity.count.badge" = "%d×"; +"bom.quantity.length.badge" = "%1$.1f %2$@"; +"bom.quantity.length.badge.with.spec" = "%1$.1f %2$@ · %3$@"; "component.fallback.name" = "Componente"; "default.load.library" = "Carga de la biblioteca"; "default.load.name" = "Mi carga"; @@ -323,3 +346,4 @@ "cable.pro.feature.dutyCycle" = "Calculadoras de cables conscientes del ciclo de trabajo"; "cable.pro.feature.batteryCapacity" = "Configura la capacidad utilizable de la batería"; "cable.pro.feature.usageBased" = "Cálculos basados en el uso"; +"generic.ok" = "Aceptar"; diff --git a/Cable/fr.lproj/Localizable.strings b/Cable/fr.lproj/Localizable.strings index f490fe2..a20a6fa 100644 --- a/Cable/fr.lproj/Localizable.strings +++ b/Cable/fr.lproj/Localizable.strings @@ -14,6 +14,29 @@ "bom.navigation.title.system" = "Liste de matériel – %@"; "bom.size.unknown" = "Taille à déterminer"; "bom.terminals.detail" = "Cosses à œillet ou à fourche adaptées aux câbles de %@"; +"bom.empty.message" = "Aucun composant enregistré pour ce système pour l’instant."; +"bom.export.pdf.button" = "Exporter en PDF"; +"bom.export.pdf.error.title" = "Échec de l’export"; +"bom.export.pdf.error.empty" = "Ajoutez au moins un composant avant l’export."; +"bom.pdf.header.title" = "Liste de matériaux du système"; +"bom.pdf.header.subtitle" = "%@ • %@"; +"bom.pdf.header.inline" = "Système d’unités : %@"; +"bom.pdf.placeholder.empty" = "Aucun composant disponible."; +"bom.pdf.page.number" = "Page %d"; +"bom.category.components.title" = "Composants et chargeurs"; +"bom.category.components.subtitle" = "Appareils principaux, contrôleurs et équipements de charge."; +"bom.category.batteries.title" = "Batteries"; +"bom.category.batteries.subtitle" = "Banques domestiques et stockage."; +"bom.category.cables.title" = "Câbles"; +"bom.category.cables.subtitle" = "Liaisons dimensionnées pour chaque circuit."; +"bom.category.fuses.title" = "Fusibles"; +"bom.category.fuses.subtitle" = "Protection des circuits et porte-fusibles."; +"bom.category.accessories.title" = "Accessoires"; +"bom.category.accessories.subtitle" = "Fusibles, cosses et pièces complémentaires."; +"bom.cable.detail.quantified" = "%1$dx %2$@"; +"bom.quantity.count.badge" = "%d×"; +"bom.quantity.length.badge" = "%1$.1f %2$@"; +"bom.quantity.length.badge.with.spec" = "%1$.1f %2$@ · %3$@"; "component.fallback.name" = "Composant"; "default.load.library" = "Charge de la bibliothèque"; "default.load.name" = "Ma charge"; @@ -322,4 +345,5 @@ "cable.pro.paywall.subtitle" = "Cable PRO offre davantage d'options de configuration pour les charges, les batteries et les chargeurs."; "cable.pro.feature.dutyCycle" = "Calculs de câbles tenant compte du cycle d'utilisation"; "cable.pro.feature.batteryCapacity" = "Configurez la capacité utilisable de la batterie"; -"cable.pro.feature.usageBased" = "Calculs basés sur l'utilisation"; +"cable.pro.feature.usageBased" = "Calculs basés sur l’utilisation"; +"generic.ok" = "OK"; diff --git a/Cable/nl.lproj/Localizable.strings b/Cable/nl.lproj/Localizable.strings index ddfdb32..f2dce3f 100644 --- a/Cable/nl.lproj/Localizable.strings +++ b/Cable/nl.lproj/Localizable.strings @@ -14,6 +14,29 @@ "bom.navigation.title.system" = "Materiaallijst – %@"; "bom.size.unknown" = "Afmeting nog onbekend"; "bom.terminals.detail" = "Ring- of vorkklemmen geschikt voor %@-bekabeling"; +"bom.empty.message" = "Er zijn nog geen componenten voor dit systeem opgeslagen."; +"bom.export.pdf.button" = "PDF exporteren"; +"bom.export.pdf.error.title" = "Export mislukt"; +"bom.export.pdf.error.empty" = "Voeg minimaal één component toe voordat je exporteert."; +"bom.pdf.header.title" = "Stuklijst van het systeem"; +"bom.pdf.header.subtitle" = "%@ • %@"; +"bom.pdf.header.inline" = "Maateenheid: %@"; +"bom.pdf.placeholder.empty" = "Geen componenten beschikbaar."; +"bom.pdf.page.number" = "Pagina %d"; +"bom.category.components.title" = "Componenten en laders"; +"bom.category.components.subtitle" = "Hoofdapparaten, regelaars en laadapparatuur."; +"bom.category.batteries.title" = "Batterijen"; +"bom.category.batteries.subtitle" = "Huishoudbanken en opslag."; +"bom.category.cables.title" = "Kabels"; +"bom.category.cables.subtitle" = "Op maat gemaakte stroomtrajecten per circuit."; +"bom.category.fuses.title" = "Zekeringen"; +"bom.category.fuses.subtitle" = "Circuitbeveiliging en houders."; +"bom.category.accessories.title" = "Accessoires"; +"bom.category.accessories.subtitle" = "Zekeringen, kabelschoenen en ondersteunende onderdelen."; +"bom.cable.detail.quantified" = "%1$dx %2$@"; +"bom.quantity.count.badge" = "%d×"; +"bom.quantity.length.badge" = "%1$.1f %2$@"; +"bom.quantity.length.badge.with.spec" = "%1$.1f %2$@ · %3$@"; "component.fallback.name" = "Component"; "default.load.library" = "Bibliotheeklast"; "default.load.name" = "Mijn last"; @@ -323,3 +346,4 @@ "cable.pro.feature.dutyCycle" = "Kabelberekeningen die rekening houden met de inschakelduur"; "cable.pro.feature.batteryCapacity" = "Configureer bruikbare batterijcapaciteit"; "cable.pro.feature.usageBased" = "Gebruiksgestuurde berekeningen"; +"generic.ok" = "OK"; diff --git a/CableTests/CableTests.swift b/CableTests/CableTests.swift index 127dcb5..3dd6675 100644 --- a/CableTests/CableTests.swift +++ b/CableTests/CableTests.swift @@ -11,40 +11,75 @@ import Testing struct CableTests { @Test func metricWireSizingUsesNearestStandardSize() async throws { - let calculator = CableCalculator() - calculator.voltage = 12 - calculator.current = 5 - calculator.length = 10 // meters - - let crossSection = calculator.recommendedCrossSection(for: .metric) + let crossSection = ElectricalCalculations.recommendedCrossSection( + length: 10, + current: 5, + voltage: 12, + unitSystem: .metric + ) #expect(crossSection == 4.0) - let voltageDrop = calculator.voltageDrop(for: .metric) + let voltageDrop = ElectricalCalculations.voltageDrop( + length: 10, + current: 5, + voltage: 12, + unitSystem: .metric + ) #expect(abs(voltageDrop - 0.425) < 0.001) - let dropPercentage = calculator.voltageDropPercentage(for: .metric) + let dropPercentage = ElectricalCalculations.voltageDropPercentage( + length: 10, + current: 5, + voltage: 12, + unitSystem: .metric + ) #expect(abs(dropPercentage - 3.5417) < 0.001) - let powerLoss = calculator.powerLoss(for: .metric) + let powerLoss = ElectricalCalculations.powerLoss( + length: 10, + current: 5, + voltage: 12, + unitSystem: .metric + ) #expect(abs(powerLoss - 2.125) < 0.001) } @Test func imperialWireSizingMatchesExpectedGauge() async throws { - let calculator = CableCalculator() - calculator.voltage = 120 - calculator.current = 15 - calculator.length = 25 // feet - - let awg = calculator.recommendedCrossSection(for: .imperial) + let awg = ElectricalCalculations.recommendedCrossSection( + length: 25, + current: 15, + voltage: 120, + unitSystem: .imperial + ) #expect(awg == 18.0) - let voltageDrop = calculator.voltageDrop(for: .imperial) + let voltageDrop = ElectricalCalculations.voltageDrop( + length: 25, + current: 15, + voltage: 120, + unitSystem: .imperial + ) #expect(abs(voltageDrop - 4.722) < 0.01) - let dropPercentage = calculator.voltageDropPercentage(for: .imperial) + let dropPercentage = ElectricalCalculations.voltageDropPercentage( + length: 25, + current: 15, + voltage: 120, + unitSystem: .imperial + ) #expect(abs(dropPercentage - 3.935) < 0.01) - let powerLoss = calculator.powerLoss(for: .imperial) + let powerLoss = ElectricalCalculations.powerLoss( + length: 25, + current: 15, + voltage: 120, + unitSystem: .imperial + ) #expect(abs(powerLoss - 70.83) < 0.05) } + + @Test func recommendedFuseRoundsUpToNearestStandardSize() async throws { + #expect(ElectricalCalculations.recommendedFuse(forCurrent: 7.2) == 10) + #expect(ElectricalCalculations.recommendedFuse(forCurrent: 59.0) == 80) + } } diff --git a/CableTests/ComponentLibraryItemTests.swift b/CableTests/ComponentLibraryItemTests.swift index 08d780c..42abbbd 100644 --- a/CableTests/ComponentLibraryItemTests.swift +++ b/CableTests/ComponentLibraryItemTests.swift @@ -1,3 +1,4 @@ +import Foundation import Testing @testable import Cable @@ -15,7 +16,7 @@ struct ComponentLibraryItemTests { affiliateLinks: [] ) - let german = Locale(identifier: "de_DE") + let german = Foundation.Locale(identifier: "de_DE") #expect(item.localizedName(for: german) == "Ankerwinde") } @@ -31,7 +32,7 @@ struct ComponentLibraryItemTests { affiliateLinks: [] ) - let french = Locale(identifier: "fr_FR") + let french = Foundation.Locale(identifier: "fr_FR") #expect(item.localizedName(for: french) == "Anchor Winch") } @@ -66,7 +67,7 @@ struct ComponentLibraryItemTests { affiliateLinks: [] ) - let spanishMexico = Locale(identifier: "es_MX") + let spanishMexico = Foundation.Locale(identifier: "es_MX") #expect(item.localizedName(for: spanishMexico) == "Molinete") } @@ -82,7 +83,7 @@ struct ComponentLibraryItemTests { affiliateLinks: [] ) - let germanSwitzerland = Locale(identifier: "de_CH") + let germanSwitzerland = Foundation.Locale(identifier: "de_CH") #expect(item.localizedName(for: germanSwitzerland) == "Ankerwinde") } @@ -98,7 +99,7 @@ struct ComponentLibraryItemTests { affiliateLinks: [] ) - let french = Locale(identifier: "fr_FR") + let french = Foundation.Locale(identifier: "fr_FR") #expect(item.localizedName(for: french) == "Guindeau") } } diff --git a/CableUITestsScreenshot/CableUITestsScreenshot.swift b/CableUITestsScreenshot/CableUITestsScreenshot.swift index 5b3d4cc..5198804 100644 --- a/CableUITestsScreenshot/CableUITestsScreenshot.swift +++ b/CableUITestsScreenshot/CableUITestsScreenshot.swift @@ -1,32 +1,536 @@ -// -// CableUITestsScreenshot.swift -// CableUITestsScreenshot -// -// Created by Stefan Lange-Hegermann on 06.10.25. -// - import XCTest final class CableUITestsScreenshot: XCTestCase { private let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") + private enum UIStringKey: String { + case addLoad + case browseLibrary + case library + case overviewTab + case componentsTab + case batteriesTab + case chargersTab + case close + case cancel + case settings + case defaultLoadName + case billOfMaterials + case systemEditorTitle + case systemsTitle + } + + private let translations: [UIStringKey: [String: String]] = [ + .addLoad: [ + "en": "Add Load", + "de": "Verbraucher hinzufügen", + "es": "Añadir carga", + "fr": "Ajouter une charge", + "nl": "Belasting toevoegen", + ], + .browseLibrary: [ + "en": "Browse Library", + "de": "Bibliothek durchsuchen", + "es": "Explorar biblioteca", + "fr": "Parcourir la bibliothèque", + "nl": "Bibliotheek bekijken", + ], + .library: [ + "en": "Library", + "de": "Bibliothek", + "es": "Biblioteca", + "fr": "Bibliothèque", + "nl": "Bibliotheek", + ], + .overviewTab: [ + "en": "Overview", + "de": "Übersicht", + "es": "Resumen", + "fr": "Aperçu", + "nl": "Overzicht", + ], + .componentsTab: [ + "en": "Components", + "de": "Verbraucher", + "es": "Componentes", + "fr": "Composants", + "nl": "Componenten", + ], + .batteriesTab: [ + "en": "Batteries", + "de": "Batterien", + "es": "Baterías", + "fr": "Batteries", + "nl": "Batterijen", + ], + .chargersTab: [ + "en": "Chargers", + "de": "Ladegeräte", + "es": "Cargadores", + "fr": "Chargeurs", + "nl": "Laders", + ], + .close: [ + "en": "Close", + "de": "Schließen", + "es": "Cerrar", + "fr": "Fermer", + "nl": "Sluiten", + ], + .cancel: [ + "en": "Cancel", + "de": "Abbrechen", + "es": "Cancelar", + "fr": "Annuler", + "nl": "Annuleren", + ], + .settings: [ + "en": "Settings", + "de": "Einstellungen", + "es": "Configuración", + "fr": "Réglages", + "nl": "Instellingen", + ], + .defaultLoadName: [ + "en": "New Load", + "de": "Neuer Verbraucher", + "es": "Carga nueva", + "fr": "Nouvelle charge", + "nl": "Nieuwe last", + ], + .billOfMaterials: [ + "en": "Bill of Materials", + "de": "Stückliste", + "es": "Lista de materiales", + "fr": "Liste de matériel", + "nl": "Stuklijst", + ], + .systemEditorTitle: [ + "en": "Edit System", + "de": "System bearbeiten", + "es": "Editar sistema", + "fr": "Modifier le système", + "nl": "Systeem bewerken", + ], + .systemsTitle: [ + "en": "Systems", + "de": "Systeme", + "es": "Sistemas", + "fr": "Systèmes", + "nl": "Systemen", + ], + ] + override func setUpWithError() throws { - continueAfterFailure = false try super.setUpWithError() - ensureDoNotDisturbEnabled() - dismissSystemOverlays() + continueAfterFailure = false + XCUIDevice.shared.orientation = .portrait + //ensureDoNotDisturbEnabled() + //dismissSystemOverlays() } override func tearDownWithError() throws { try super.tearDownWithError() - dismissSystemOverlays() + //dismissSystemOverlays() } @MainActor - func testExample() throws { + func testOnboardingScreenshots() throws { + let app = launchApp(arguments: ["--uitest-reset-data"]) + waitForStability(long: true) + dismissNotificationBannersIfNeeded() + waitForStability(long: true) + dismissNotificationBannersIfNeeded() + waitForStability(long: true) + dismissNotificationBannersIfNeeded() + waitForStability(long: true) + dismissNotificationBannersIfNeeded() + + let createSystemButton = app.buttons["create-system-button"] + XCTAssertTrue(createSystemButton.waitForExistence(timeout: 8)) + waitForStability(long: true) + dismissNotificationBannersIfNeeded() + waitForStability(long: true) + takeScreenshot(named: "01-OnboardingSystemsView") + + createSystemButton.tap() + + let addLoadButton = button(in: app.buttons, for: .addLoad) + XCTAssertTrue(addLoadButton.waitForExistence(timeout: 8)) + let browseLibraryButton = button(in: app.buttons, for: .browseLibrary) + XCTAssertTrue(browseLibraryButton.waitForExistence(timeout: 4)) + + waitForStability() + takeScreenshot(named: "02-OnboardingSystemView") + + browseLibraryButton.tap() + let libraryCloseButton = app.buttons["library-view-close-button"] + XCTAssertTrue(libraryCloseButton.waitForExistence(timeout: 8)) + waitForStability(long: true) + takeScreenshot(named: "04-ComponentSelectorView") + libraryCloseButton.tap() + + XCTAssertTrue(addLoadButton.waitForExistence(timeout: 4)) + addLoadButton.tap() + + let newLoadButton = button(in: app.buttons, for: .defaultLoadName) + XCTAssertTrue(newLoadButton.waitForExistence(timeout: 8)) + waitForStability(long: true) + takeScreenshot(named: "03-LoadEditorView") + } + + @MainActor + func testSampleDataScreenshots() throws { let app = XCUIApplication() + app.launchArguments = ["--uitest-reset-data", "--uitest-sample-data"] app.launch() - dismissSystemOverlays() + + let systemsList = resolvedSystemsList(in: app) + XCTAssertTrue(systemsList.waitForExistence(timeout: 4)) + takeScreenshot(named: "05-SystemsWithSampleData") + + let firstSystemCell = systemsList.cells.element(boundBy: 0) + XCTAssertTrue(firstSystemCell.waitForExistence(timeout: 2)) + let systemName = firstSystemCell.staticTexts.firstMatch.label + + let systemButton = firstSystemCell.buttons.firstMatch + if systemButton.exists { + systemButton.tap() + } else { + firstSystemCell.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + } + + var detailVisible = waitForSystemDetail(named: systemName, in: app) + if !detailVisible { + firstSystemCell.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + detailVisible = waitForSystemDetail(named: systemName, in: app) + } + XCTAssertTrue(detailVisible) + takeScreenshot(named: "06-AdventureVanOverview") + +// let overviewTab = app.buttons["overview-tab"] +// XCTAssertTrue(overviewTab.waitForExistence(timeout: 3)) +// overviewTab.tap() + waitForStability(long: false) + let bomElement = resolveBillOfMaterialsElement(in: app) + + if !bomElement.waitForExistence(timeout: 6) { + bringElementIntoView(bomElement, in: app) + } + + XCTAssertTrue(bomElement.exists) + + if !bomElement.isHittable { + bringElementIntoView(bomElement, in: app, requireHittable: true) + } + + if bomElement.isHittable { + bomElement.tap() + } else { + bomElement.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + } + waitForStability(long: true) + takeScreenshot(named: "08-BillOfMaterials") + + let closeButton = app.buttons["system-bom-close-button"] + XCTAssertTrue(closeButton.waitForExistence(timeout: 6)) + closeButton.tap() + + let componentsTab = componentsTabButton(in: app) + XCTAssertTrue(componentsTab.waitForExistence(timeout: 3)) + if componentsTab.isHittable { + componentsTab.tap() + } else { + componentsTab.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + } + + let loadsList = resolvedLoadsList(in: app) + XCTAssertTrue(loadsList.waitForExistence(timeout: 4)) + takeScreenshot(named: "07-AdventureVanLoads") + + waitForStability() + let firstLoad = loadsList.cells.element(boundBy: 0) + XCTAssertTrue(firstLoad.waitForExistence(timeout: 2)) + let loadName = firstLoad.staticTexts.firstMatch.label + firstLoad.tap() + + let loadNavButton = app.navigationBars.buttons[loadName] + XCTAssertTrue(loadNavButton.waitForExistence(timeout: 3)) + takeScreenshot(named: "09-AdventureVanCalculator") + } + + private func launchApp(arguments: [String]) -> XCUIApplication { + let app = XCUIApplication() + var launchArguments = ["--uitest-reset-data"] + launchArguments.append(contentsOf: arguments) + app.launchArguments = launchArguments + app.launch() + //dismissSystemOverlays() + return app + } + + private func resolvedSystemsList(in app: XCUIApplication) -> XCUIElement { + let collection = app.collectionViews["systems-list"] + if collection.waitForExistence(timeout: 6) { + return collection + } + + if app.collectionViews.firstMatch.waitForExistence(timeout: 2) { + return app.collectionViews.firstMatch + } + + let table = app.tables["systems-list"] + if table.waitForExistence(timeout: 6) { + return table + } + + XCTAssertTrue(app.tables.firstMatch.waitForExistence(timeout: 2)) + return app.tables.firstMatch + } + + private func resolvedLoadsList(in app: XCUIApplication) -> XCUIElement { + let collection = app.collectionViews["loads-list"] + if collection.waitForExistence(timeout: 6) { + return collection + } + + let table = app.tables["loads-list"] + XCTAssertTrue(table.waitForExistence(timeout: 6)) + return table + } + + private func takeScreenshot(named name: String) { + let screenshot = XCUIScreen.main.screenshot() + let attachment = XCTAttachment(screenshot: screenshot) + attachment.name = name + attachment.lifetime = .keepAlways + add(attachment) + } + + private func waitForStability(long: Bool = false) { + RunLoop.current.run(until: Date().addingTimeInterval(long ? 5.0 : 0.5)) + } + + private func componentsTabButton(in app: XCUIApplication) -> XCUIElement { + let identifierMatch = app.descendants(matching: .any) + .matching(identifier: "components-tab").firstMatch + if identifierMatch.exists { + return identifierMatch + } + + let localizedLabels = [ + "Components", "Verbraucher", "Componentes", "Composants", "Componenten" + ] + for label in localizedLabels { + let button = app.buttons[label] + if button.exists { + return button + } + + let tabBarButton = app.tabBars.buttons[label] + if tabBarButton.exists { + return tabBarButton + } + + let segmentedButton = app.segmentedControls.buttons[label] + if segmentedButton.exists { + return segmentedButton + } + + let segmentedOther = app.segmentedControls.otherElements[label] + if segmentedOther.exists { + return segmentedOther + } + } + + let fallbackSegmented = app.segmentedControls.buttons.element(boundBy: 1) + if fallbackSegmented.exists { + return fallbackSegmented + } + + let tabBarButton = app.tabBars.buttons.element(boundBy: 1) + if tabBarButton.exists { + return tabBarButton + } + + return app.tabBars.descendants(matching: .any).firstMatch + } + + private func waitForSystemDetail(named systemName: String, in app: XCUIApplication, timeout: TimeInterval = 6) -> Bool { + let deadline = Date().addingTimeInterval(timeout) + while Date() < deadline { + if app.otherElements["system-overview"].exists { + return true + } + + let navBar = app.navigationBars.firstMatch + if navBar.buttons[systemName].exists || navBar.staticTexts[systemName].exists { + return true + } + + RunLoop.current.run(until: Date().addingTimeInterval(0.25)) + } + + return app.otherElements["system-overview"].exists + } + + private func bringElementIntoView( + _ element: XCUIElement, + in app: XCUIApplication, + requireHittable: Bool = false, + attempts: Int = 8 + ) { + let scrollContainer = app.scrollViews.firstMatch.exists ? app.scrollViews.firstMatch : app.tables.firstMatch + for _ in 0.. XCUIElement { + let identifier = "system-bom-button" + let buttonByIdentifier = app.buttons.matching(identifier: identifier).firstMatch + if buttonByIdentifier.exists { return buttonByIdentifier } + + let elementByIdentifier = app.otherElements.matching(identifier: identifier).firstMatch + if elementByIdentifier.exists { return elementByIdentifier } + + let candidates = candidateStrings(for: .billOfMaterials) + for candidate in candidates { + let button = app.buttons[candidate] + if button.exists { + return button + } + let other = app.otherElements[candidate] + if other.exists { + return other + } + } + + return buttonByIdentifier + } + + private func dismissNotificationBannersIfNeeded() { + let banner = springboard.otherElements.matching(identifier: "NotificationShortLookView").firstMatch + if banner.waitForExistence(timeout: 1) { + if banner.isHittable { + banner.swipeUp() + } else { + let start = banner.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) + let end = banner.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: -1.5)) + start.press(forDuration: 0.05, thenDragTo: end) + } + waitForStability() + } + } + + private func candidateStrings(for key: UIStringKey) -> [String] { + var values = Set() + if let languageCode = Locale.preferredLanguages.first.flatMap({ String($0.prefix(2)) }), + let localized = translations[key]?[languageCode] { + values.insert(localized) + } + if let english = translations[key]?["en"] { + values.insert(english) + } + if let others = translations[key]?.values { + values.formUnion(others) + } + if key == .settings { + values.insert("gearshape") + } + return Array(values) + } + + private func button(in query: XCUIElementQuery, for key: UIStringKey) -> XCUIElement { + let candidates = candidateStrings(for: key) + for candidate in candidates { + let element = query[candidate] + if element.exists { + return element + } + } + let predicate = NSPredicate( + format: "label IN %@ OR identifier IN %@", + NSArray(array: candidates), + NSArray(array: candidates) + ) + return query.matching(predicate).firstMatch + } + + private func optionalButton(in query: XCUIElementQuery, for key: UIStringKey) -> XCUIElement? { + let element = button(in: query, for: key) + return element.exists ? element : nil + } + + private func tabButton(in app: XCUIApplication, key: UIStringKey) -> XCUIElement { + let tabSpecific = button(in: app.tabBars.buttons, for: key) + if tabSpecific.exists { + return tabSpecific + } + return button(in: app.buttons, for: key) + } + + private func navigationBar(in app: XCUIApplication, key: UIStringKey) -> XCUIElement { + let candidates = candidateStrings(for: key) + for candidate in candidates { + let bar = app.navigationBars[candidate] + if bar.exists { + return bar + } + } + return app.navigationBars.element(boundBy: 0) + } + + private func tapButtonIfPresent(app: XCUIApplication, key: UIStringKey) { + let candidates = candidateStrings(for: key) + for candidate in candidates { + let button = app.buttons[candidate] + if button.waitForExistence(timeout: 2) { + button.tap() + return + } + } + } + + private func openBillOfMaterials(app: XCUIApplication) { + let bomButton = button(in: app.buttons, for: .billOfMaterials) + XCTAssertTrue(bomButton.waitForExistence(timeout: 6)) + bomButton.tap() + let bomView = app.otherElements["system-bom-view"] + XCTAssertTrue(bomView.waitForExistence(timeout: 8)) + waitForStability(long: true) + } + + private func closeBillOfMaterials(app: XCUIApplication) { + tapButtonIfPresent(app: app, key: .close) + } + + private func navigateBack(app: XCUIApplication) { + let backButton = app.navigationBars.buttons.element(boundBy: 0) + if backButton.exists { + backButton.tap() + } else { + app.swipeRight() + } + } + + private func openSettings(app: XCUIApplication) { + let systemsBar = navigationBar(in: app, key: .systemsTitle) + let settingsButton = button(in: systemsBar.buttons, for: .settings) + if settingsButton.exists { + settingsButton.tap() + } else { + systemsBar.buttons.element(boundBy: 0).tap() + } } private func ensureDoNotDisturbEnabled() { @@ -41,6 +545,8 @@ final class CableUITestsScreenshot: XCTestCase { focusTile.press(forDuration: 1.0) } else if focusButton.waitForExistence(timeout: 2) { focusButton.press(forDuration: 1.0) + } else { + return } let dndButton = springboard.buttons["Do Not Disturb"] diff --git a/CableUITestsScreenshot/CableUITestsScreenshotLaunchTests.swift b/CableUITestsScreenshot/CableUITestsScreenshotLaunchTests.swift index bf2800c..b147558 100644 --- a/CableUITestsScreenshot/CableUITestsScreenshotLaunchTests.swift +++ b/CableUITestsScreenshot/CableUITestsScreenshotLaunchTests.swift @@ -8,7 +8,37 @@ import XCTest final class CableUITestsScreenshotLaunchTests: XCTestCase { - + private func launchApp(arguments: [String] = []) -> XCUIApplication { + let app = XCUIApplication() + var launchArguments = ["--uitest-reset-data"] + launchArguments.append(contentsOf: arguments) + app.launchArguments = launchArguments + app.launch() + return app + } + + private func resolvedSystemsList(in app: XCUIApplication) -> XCUIElement { + let collection = app.collectionViews["systems-list"] + if collection.waitForExistence(timeout: 6) { + return collection + } + + let table = app.tables["systems-list"] + XCTAssertTrue(table.waitForExistence(timeout: 6)) + return table + } + + private func resolvedLoadsList(in app: XCUIApplication) -> XCUIElement { + let collection = app.collectionViews["loads-list"] + if collection.waitForExistence(timeout: 6) { + return collection + } + + let table = app.tables["loads-list"] + XCTAssertTrue(table.waitForExistence(timeout: 6)) + return table + } + private func takeScreenshot(name: String, lifetime: XCTAttachment.Lifetime = .keepAlways) { @@ -30,66 +60,63 @@ final class CableUITestsScreenshotLaunchTests: XCTestCase { @MainActor func testOnboardingLoadsView() throws { - let app = XCUIApplication() - - app.launch() + let app = launchApp(arguments: ["--uitest-reset-data"]) takeScreenshot(name: "01-OnboardingSystemsView") - + let createSystemButton = app.buttons["create-system-button"] XCTAssertTrue(createSystemButton.waitForExistence(timeout: 5)) createSystemButton.tap() takeScreenshot(name: "02-OnboardingLoadsView") + let componentsTab = app.buttons["components-tab"] + XCTAssertTrue(componentsTab.waitForExistence(timeout: 3)) + componentsTab.tap() + let libraryCloseButton = app.buttons["library-view-close-button"] - let selectComponentButton = app.buttons["select-component-button"] - XCTAssertTrue(selectComponentButton.waitForExistence(timeout: 5)) - selectComponentButton.tap() + let browseLibraryButton = onboardingSecondaryButton(in: app) + XCTAssertTrue(browseLibraryButton.waitForExistence(timeout: 5)) + browseLibraryButton.tap() XCTAssertTrue(libraryCloseButton.waitForExistence(timeout: 5)) Thread.sleep(forTimeInterval: 10) takeScreenshot(name: "04-ComponentSelectorView") libraryCloseButton.tap() - - let createComponentButton = app.buttons["create-component-button"] + + let createComponentButton = onboardingPrimaryButton(in: app) XCTAssertTrue(createComponentButton.waitForExistence(timeout: 5)) createComponentButton.tap() takeScreenshot(name: "03-LoadEditorView") } - + func testWithSampleData() throws { - let app = XCUIApplication() - app.launchArguments.append("--uitest-sample-data") - app.launch() + let app = launchApp(arguments: ["--uitest-sample-data"]) - let systemsCollection = app.collectionViews.firstMatch - let collectionExists = systemsCollection.waitForExistence(timeout: 3) - - let systemsList: XCUIElement - if collectionExists { - systemsList = systemsCollection - } else { - let table = app.tables.firstMatch - XCTAssertTrue(table.waitForExistence(timeout: 3)) - systemsList = table - } + let systemsList = resolvedSystemsList(in: app) let firstSystemCell = systemsList.cells.element(boundBy: 0) XCTAssertTrue(firstSystemCell.waitForExistence(timeout: 3)) + let systemName = firstSystemCell.staticTexts.firstMatch.label takeScreenshot(name: "05-SystemsWithSampleData") - firstSystemCell.tap() - - let loadsCollection = app.collectionViews["loads-list"] - let loadsTable = app.tables["loads-list"] - - let loadsElement: XCUIElement - if loadsCollection.waitForExistence(timeout: 3) { - loadsElement = loadsCollection + let rowButton = firstSystemCell.buttons.firstMatch + if rowButton.waitForExistence(timeout: 2) { + rowButton.tap() } else { - XCTAssertTrue(loadsTable.waitForExistence(timeout: 3)) - loadsElement = loadsTable + firstSystemCell.tap() } + let navButton = app.navigationBars.buttons[systemName] + if !navButton.waitForExistence(timeout: 3) { + let coordinate = firstSystemCell.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) + coordinate.tap() + XCTAssertTrue(navButton.waitForExistence(timeout: 3)) + } + + tapComponentsTab(in: app) + + let loadsElement = resolvedLoadsList(in: app) + XCTAssertTrue(loadsElement.waitForExistence(timeout: 6)) + XCTAssertTrue(loadsElement.cells.firstMatch.waitForExistence(timeout: 3)) Thread.sleep(forTimeInterval: 1) takeScreenshot(name: "06-AdventureVanLoads") @@ -98,9 +125,42 @@ final class CableUITestsScreenshotLaunchTests: XCTestCase { XCTAssertTrue(bomButton.waitForExistence(timeout: 2)) bomButton.tap() - let bomView = app.otherElements["system-bom-view"] - XCTAssertTrue(bomView.waitForExistence(timeout: 3)) - Thread.sleep(forTimeInterval: 1) - takeScreenshot(name: "07-AdventureVanBillOfMaterials") +// let bomView = app.otherElements["system-bom-view"] +// XCTAssertTrue(bomView.waitForExistence(timeout: 3)) +// +// Thread.sleep(forTimeInterval: 1) +// takeScreenshot(name: "07-AdventureVanBillOfMaterials") + } + + private func tapComponentsTab(in app: XCUIApplication) { + let button = componentsTabButton(in: app) + XCTAssertTrue(button.waitForExistence(timeout: 3)) + button.tap() + } + + private func onboardingPrimaryButton(in app: XCUIApplication) -> XCUIElement { + let button = app.buttons["create-component-button"] + if button.exists { return button } + return app.buttons["onboarding-primary-button"] + } + + private func onboardingSecondaryButton(in app: XCUIApplication) -> XCUIElement { + let button = app.buttons["select-component-button"] + if button.exists { return button } + return app.buttons["onboarding-secondary-button"] + } + + private func componentsTabButton(in app: XCUIApplication) -> XCUIElement { + let idButton = app.buttons["components-tab"] + if idButton.exists { + return idButton + } + + let labels = ["Components", "Verbraucher", "Componentes", "Composants", "Componenten"] + for label in labels { + let button = app.buttons[label] + if button.exists { return button } + } + return app.tabBars.buttons.element(boundBy: 1) } } diff --git a/Podfile b/Podfile index 3a9e543..f821fad 100644 --- a/Podfile +++ b/Podfile @@ -6,7 +6,6 @@ target 'Cable' do use_frameworks! # Pods for Cable - pod "PostHog", "~> 3.0" target 'CableTests' do inherit! :search_paths # Pods for testing diff --git a/Podfile.lock b/Podfile.lock deleted file mode 100644 index 506b37a..0000000 --- a/Podfile.lock +++ /dev/null @@ -1,16 +0,0 @@ -PODS: - - PostHog (3.34.0) - -DEPENDENCIES: - - PostHog (~> 3.0) - -SPEC REPOS: - trunk: - - PostHog - -SPEC CHECKSUMS: - PostHog: bbb7eaecb2f5a286d9da3c833cbb18ae08799655 - -PODFILE CHECKSUM: b59bd921fb9a91e981795c18b6ff238370434172 - -COCOAPODS: 1.16.2 diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock deleted file mode 100644 index 506b37a..0000000 --- a/Pods/Manifest.lock +++ /dev/null @@ -1,16 +0,0 @@ -PODS: - - PostHog (3.34.0) - -DEPENDENCIES: - - PostHog (~> 3.0) - -SPEC REPOS: - trunk: - - PostHog - -SPEC CHECKSUMS: - PostHog: bbb7eaecb2f5a286d9da3c833cbb18ae08799655 - -PODFILE CHECKSUM: b59bd921fb9a91e981795c18b6ff238370434172 - -COCOAPODS: 1.16.2 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj deleted file mode 100644 index 8c7866a..0000000 --- a/Pods/Pods.xcodeproj/project.pbxproj +++ /dev/null @@ -1,2254 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 01C82EF77CAD48D420201E5041F71981 /* backward_references_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 873422C94FE37539EBA965E98C7A3636 /* backward_references_enc.c */; }; - 01EB398AE7FB8D1AA43CCC4BE25256A1 /* PostHogTagViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 267A77D966A9CB080522F80053CB2597 /* PostHogTagViewModifier.swift */; }; - 0357CEDE60497FDDDFF4206D50D84D32 /* dec_sse41.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CECA788D22A8D210E1F126F087FA049 /* dec_sse41.c */; }; - 0572679AE85C33C43DE798C06F3E1534 /* ph_huffman_encode_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = F115DBC1009A4AE8B1E2EEACECEE686D /* ph_huffman_encode_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 06F57CD4670CD1DAE91524790BFC6EC1 /* EdgeBorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = BADD4B93BF926C2B8CA6E8BAC40F9EED /* EdgeBorder.swift */; }; - 07169E6F462DE20D301856B85FB0C74F /* rescaler_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = 8B72D15908356213E83617F069F28778 /* rescaler_neon.c */; }; - 0867541398BCAD5D8D499F66BB8887FB /* RRStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC9E8EDC00E76181958B8A45361E2E3 /* RRStyle.swift */; }; - 0A24C32E44D0415EF08780849822BCEE /* yuv_sse41.c in Sources */ = {isa = PBXBuildFile; fileRef = 30238EC8C0FAB5B9682951C8C40A56BC /* yuv_sse41.c */; }; - 0AE01E9EDF9EC4A863872FB71F99983B /* View+PostHogLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9145EC49215D8AD4B67DC4C8A3FFCD24 /* View+PostHogLabel.swift */; }; - 0B03D2F73EAB1910FB04EFA201F7FA59 /* enc_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = E634ED9E4F291E94869F8F535E2FB59D /* enc_neon.c */; }; - 0BE2AAAE30AE1B29B23B1E55C819D7A7 /* PostHogPersonProfiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED8BAA4BA5693210FDA9BE42AA76F93E /* PostHogPersonProfiles.swift */; }; - 0C4DEC31440C7333BC97E7B78D67BC72 /* tree_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = B4AE78BA77D7992329434D3A2D3370F6 /* tree_enc.c */; }; - 0E16356A19CF2C4ED7FE29BBB4F700BC /* random_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 276E633F3287EC40CFDCCA704F85A7BB /* random_utils.c */; }; - 0E3BEE1E611D26371DAF50C23E7AC1BB /* UIImage+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 497BF2253E737EA49DACF8B6460E188A /* UIImage+Util.swift */; }; - 0EF0A53B84657EF4A4A6C6E11C409DF3 /* yuv_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = 235F09390CCC154EF3CE1AF84501B9D5 /* yuv_neon.c */; }; - 0F409AE2D56C546B8A1155D37CCA80D5 /* PostHogConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C042A1CBFE280735CE602F869E5E16E /* PostHogConfig.swift */; }; - 1089A5E22CB40877785F1B4D5B8337D8 /* dec_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 1B3EF635F2042C38745AADD3B049F9D1 /* dec_sse2.c */; }; - 125C8B61C19411B4E222E2D4670EDA7C /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F97F6C297E221C6D17A28FD64FD56C9 /* ReadWriteLock.swift */; }; - 13D9AE4C00CE2535E0DEF135306CB8BF /* PostHogDisplaySurveyQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CD1BA30ECCBAFA941F6D593A91CE5A /* PostHogDisplaySurveyQuestion.swift */; }; - 13F0B4871E8EB793200587DF5C1C4246 /* PostHogSessionReplayPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B180FB52B54B3D7CE6A1EB92AF8CD1F /* PostHogSessionReplayPlugin.swift */; }; - 141DFED4583A49BE8122CF85D9C530B2 /* Pods-Cable-CableUITestsScreenshot-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 701F2C64F76028BD63C8BC57E763B989 /* Pods-Cable-CableUITestsScreenshot-dummy.m */; }; - 144C6DD1A247AC1EDF9AD57981DCB922 /* ph_palette.h in Headers */ = {isa = PBXBuildFile; fileRef = A97D7894341E91B3BF09503921D83C16 /* ph_palette.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 152739ADAFEA0909584A96C79E07C151 /* PostHogReplayIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36EBA9BB4949682419A03F0FE285AFDD /* PostHogReplayIntegration.swift */; }; - 15F7984FF736D8AF019F417DA8901036 /* PostHog-PostHog in Resources */ = {isa = PBXBuildFile; fileRef = 42D87DCAF52175DC2D5C6B44419636A8 /* PostHog-PostHog */; }; - 16053DB437B9398112A24A047C55D124 /* PostHogApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A8552B95757A4F529060DCD92FA9EA8 /* PostHogApi.swift */; }; - 183E1F870F4FDDEA7C57FEC6BD70ED5B /* near_lossless_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = B3892D9BAABC94C6D1D38A736617D726 /* near_lossless_enc.c */; }; - 18BF9FAC9C9D784C748A0AFEE36F20E1 /* PostHogBatchUploadInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DAC933623085F073616F04099AF6703 /* PostHogBatchUploadInfo.swift */; }; - 19D82E01983174C6A45C7EE85AA81795 /* UITextInputTraits+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB59137CCCF0AB605EA3C16F30B47C7 /* UITextInputTraits+Util.swift */; }; - 1C54E6F4D075181AE6E195DC2CBA737D /* QuestionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A84BF685A521744850F13C55520AAB70 /* QuestionHeader.swift */; }; - 1ED830E9C2C477B7C3C88115DEB77BA4 /* ph_decode.h in Headers */ = {isa = PBXBuildFile; fileRef = D396A5F4E5EA6A631CFA1F5BA73BE979 /* ph_decode.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 236BD01A81BCD619F470D17D00FA2440 /* UIWindow+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D1B502E8A8D8A7CF0F160485F27D94 /* UIWindow+.swift */; }; - 2468C99062231929D4FA5A274E16CB72 /* AssociatedKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15861BEA1F90094B56DC2CD840469B3D /* AssociatedKeys.swift */; }; - 24DEE46DD19D816066C36A6F5AFF3D20 /* PostHog-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 74853741E05F6EF1BEB0A51503A2FAAF /* PostHog-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 252C54D4211DE3406B82DC8206E354DA /* PostHogLogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C720DA9EA026D5E7449597140583E063 /* PostHogLogLevel.swift */; }; - 2599E595E56867C2CC371352E74E5CB0 /* PostHogSurveyResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73BBDBBF3DD175220822CBE95FE0B6DA /* PostHogSurveyResponse.swift */; }; - 273B9F6A4399EA87F762570917E52D34 /* Hedgelog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C3B8263979EC31683158AC2B8D195C7 /* Hedgelog.swift */; }; - 287E75C60B7FB8A9A9E6A4D5E2465701 /* upsampling.c in Sources */ = {isa = PBXBuildFile; fileRef = 5C1E2A40BEFB3E27559CF75D3AF598D0 /* upsampling.c */; }; - 2B18064A9F0190519F509DD5891B81A0 /* PostHogRemoteConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05688B486C05767CC332F7BF18F63FA6 /* PostHogRemoteConfig.swift */; }; - 2C34DA75C8E0FD9A4A61AF56826CE149 /* predictor_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 7309703A36FD4345F1782408988D9A2A /* predictor_enc.c */; }; - 2C4358D6D544B725F9B1EB1A3FF6D35B /* rescaler_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 3EA95D0670D5D47F7BC69002970C0E15 /* rescaler_sse2.c */; }; - 308067A90C6D4714A6625855CF82A55C /* ph_bit_reader_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 114279FBFB8CEFB0597BCACAFD61F21C /* ph_bit_reader_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 30C93CCDE28363680E50EA3A91FE8B0A /* Optional+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5EDD8394DA064EC7C83ACD7ECCD71E /* Optional+Util.swift */; }; - 3155F7F3D2B67132C14891B5A2E8F65B /* alpha_processing_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = 6293FFF4CAA83AC8938A5AF805A16B4E /* alpha_processing_neon.c */; }; - 32AECF33E41A6D0971F70B1220A182F5 /* URLSessionInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439F6F8B57A9D0DC1E4C4669A0B6BD70 /* URLSessionInterceptor.swift */; }; - 32BD682BFDA6241281CEBA7904C5A5A1 /* PostHogContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94407C342DD9E167C5D75E705899A981 /* PostHogContext.swift */; }; - 3491FB1468B98D7D43A3D900A0B00281 /* sharpyuv.c in Sources */ = {isa = PBXBuildFile; fileRef = DF2A3A811E0015722FF7E34E9338B448 /* sharpyuv.c */; }; - 360ABBEBC1B15FDCF4DA237E7647F2BE /* sharpyuv_csp.c in Sources */ = {isa = PBXBuildFile; fileRef = B2FFDD72D120FD8743D882769EF18E9B /* sharpyuv_csp.c */; }; - 36C07E870675759BC50B2A25CCC281C5 /* Pods-CableTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 85828150034429DD2DBC8A14669BDF37 /* Pods-CableTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 38177A6BBDA55B9E965610C950A24204 /* PostHogSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF48F37324DDA42ABC08BC0E63AE23FC /* PostHogSDK.swift */; }; - 38AF18D906A0862245B23AA744DEFE31 /* lossless.c in Sources */ = {isa = PBXBuildFile; fileRef = F553AE255E9F4CBC202ED457EF76082F /* lossless.c */; }; - 38E6D9105E78ED4CC0E127C62F53B331 /* Pods-Cable-CableUITests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F57C7D4BAC17BBF066E3F6D4C4B59A /* Pods-Cable-CableUITests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 395B15A53215A65E19F9C6A19D422D3A /* ph_random_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = A64CAB6E8B5412C7633F7B8565B8651D /* ph_random_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39FD34007D0F0D009C7F332C7DC5E187 /* sharpyuv_cpu.c in Sources */ = {isa = PBXBuildFile; fileRef = 79C664EEE1DC1B7E4C1AD3FFBCE62DCF /* sharpyuv_cpu.c */; }; - 3A184F0C63E5C112D93A58B9032A6B58 /* ph_webpi_dec.h in Headers */ = {isa = PBXBuildFile; fileRef = 44A2B199B29D72CB41D7DCC66EE77960 /* ph_webpi_dec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3B1D530F1C85B1227913F0026BFD0BDC /* UUIDUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39451BDDC73A8B6E24FFE97D8F418255 /* UUIDUtils.swift */; }; - 3C960D07046A6486F7DC7FD20FC938FA /* ph_sharpyuv_gamma.h in Headers */ = {isa = PBXBuildFile; fileRef = C1C1F47ADEAE9E1F0B9810DF4E04BD56 /* ph_sharpyuv_gamma.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3E0A9BA7670824E8A542BB0238965308 /* URLSessionSwizzler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CACEF186F8DF80FAFD17F7D33BC5D71 /* URLSessionSwizzler.swift */; }; - 3E4221A803DEDC8750348BA20A791EEE /* PostHogSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D888AE3CE3943C819B306C6E7A8FF6BC /* PostHogSessionManager.swift */; }; - 4006CE8FE027629C962E1BFB2A56F858 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */; }; - 40BD75D3EE82B288BCAA04D3DACEBD99 /* Survey+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26FE4D61E0E5E088E0A627B0FDFC294E /* Survey+Util.swift */; }; - 4238E88AE8BA8624075729D326C0F4A9 /* alpha_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 02F6988AFBC0B2AF511F86755F935B13 /* alpha_enc.c */; }; - 438E9D989048FFC3DF68443837BF6398 /* PostHogExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7C8924423D336E936B9B8D7528391B5 /* PostHogExtensions.swift */; }; - 4391AA0D63D56F3F6233972901D35B62 /* bit_writer_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9FBABCFADE0E13362BC515109844B0B7 /* bit_writer_utils.c */; }; - 43DBAA9AC5E2E63021F065F66FCBC065 /* ApplicationEventPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400531B7575C215F76B0965A3F16DDA0 /* ApplicationEventPublisher.swift */; }; - 44058A91D10EDA3415018DE7925C348B /* SurveySheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6304E8E5FDED5863B9DCE550E79F2F13 /* SurveySheet.swift */; }; - 450E0503FAA2B07BD4D1F2C3CB11B122 /* picture_rescale_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BBC9AA886853862CBA71BF1966D0F2 /* picture_rescale_enc.c */; }; - 453AF00CBDEE42C069369E1F1BB482FC /* ph_common_sse41.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B8A9E04574F84317E10956DD24EE006 /* ph_common_sse41.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 475FD727732636A7758AA528CA362744 /* ph_huffman_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = DE79FAF47ADBBDA1FD96FA4EB80CD2D4 /* ph_huffman_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4805E842E48C745A9E6A4C06D4731373 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = AC3ADEA740D3607618548A1451E8EE35 /* PrivacyInfo.xcprivacy */; }; - 48B8B5B751C3E55843A697C3792C7837 /* palette.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B69F8B2559E5EE459C85C21EFEE836B /* palette.c */; }; - 493D47C4E0ADF0AA3C30D3FB6C2F9C57 /* CGSize+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CAF2DEC195826ABDDD705FA32B3BAC /* CGSize+Util.swift */; }; - 4A2589F3E16BE0902709FF2A3630BE17 /* SurveyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262A25E1DBCEB2502F06974ED0FE5FBD /* SurveyButton.swift */; }; - 4BB3C8C41EA289FB29DCEC068F7FCD76 /* UIImage+WebP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5131F1FFA6BB84612F7867F86C51FCBE /* UIImage+WebP.swift */; }; - 4DE8C331A0B2A6959482C25ACB77E609 /* ph_sharpyuv_cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = BCC15FDAC7750FBA4BA88CC539B5231E /* ph_sharpyuv_cpu.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4E1473B9FF22300F21A2ABCA3911A03B /* picture_csp_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 94288A5DC18B54C6DC2DD8C571B0F0F6 /* picture_csp_enc.c */; }; - 4E33D63DE814B6144A257A9CBE8B695C /* lossless_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = A296C0A01AD7CFE6D2C8A9367A6C2A67 /* lossless_neon.c */; }; - 4EA5892CD434E01818878C81F1452F65 /* ph_sharpyuv_dsp.h in Headers */ = {isa = PBXBuildFile; fileRef = EEA4A4F055CC07589E72A35CB0E1EF28 /* ph_sharpyuv_dsp.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4ED6FC8835C4F8D158F713E6D7E8BB76 /* sharpyuv_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = 77C6B393EA1E57769EC0389F1AD05B9C /* sharpyuv_neon.c */; }; - 4F5F04A349551F176FFCF2A5F318055D /* token_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 04F316A4B2CB95903F1476FD8F6E5506 /* token_enc.c */; }; - 4F61F0B687C035589D0FD4E29A73B0DF /* ph_neon.h in Headers */ = {isa = PBXBuildFile; fileRef = A4B8C487F55A8DC54688605DFF2114A6 /* ph_neon.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4FA4C53C4992B0098AACCF9805B840B3 /* UIView+PostHogLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 394497CE62FB8F5E127D55A66D718803 /* UIView+PostHogLabel.swift */; }; - 50B1BD836429AB01BD316C26265CD71C /* huffman_encode_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D78E84DEDA315BA0155AF3658637CFC /* huffman_encode_utils.c */; }; - 51557FD624B2D8D939BD15D6D67DAA9F /* AutocaptureEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B671EE629623603942CB46DA763609 /* AutocaptureEventProcessing.swift */; }; - 51F949044932E2740E23BC026B3CB070 /* DI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DB3C2029FEE43C9492E48815A749CD8 /* DI.swift */; }; - 526DF460F26A30E9A4C72AE94B898DAC /* BottomSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50567BD13E8812DF3257A08243491EA /* BottomSection.swift */; }; - 52D4C546D7A305CC6D9B29EBBE6BA5DA /* DateUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 812FDF6DD4F545B16DF167DAEA77C36B /* DateUtils.swift */; }; - 538F223AE0B8D0CA9A7DFF6C1BCDF61A /* PostHogConsoleLogInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE54094ABB405AD044CBC194CBC3787A /* PostHogConsoleLogInterceptor.swift */; }; - 54E24A21DBE22F50FF2C8ED9FE8C1920 /* ph_cost_enc.h in Headers */ = {isa = PBXBuildFile; fileRef = FBA8E6C91C9871D781A894E9021E767F /* ph_cost_enc.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 550CB5E2CAB7E6F88A94256F8D4E1AD0 /* lossless_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = E0D796ED35657AF32721999BD872B40E /* lossless_enc.c */; }; - 560F19C3A96258BB787771F53438277C /* PostHogSurveysDefaultDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57AED642173AA14A5AB8433E3D4B85ED /* PostHogSurveysDefaultDelegate.swift */; }; - 569413C3DF6867A0AF74DE0A54B21317 /* ph_vp8li_enc.h in Headers */ = {isa = PBXBuildFile; fileRef = 268E4C94970868F0267B55DD268C4FDC /* ph_vp8li_enc.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 570EB0858F1AFE99A8B7CA45C6C3BAEE /* PostHogScreenViewIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49AA24A61F055E7B69C6A5D8C6B2AD78 /* PostHogScreenViewIntegration.swift */; }; - 580FDEB8A8A152A39A502A40A58724C2 /* PostHogStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B7E943BABB7F3EAC5DE3E570F21D845 /* PostHogStorage.swift */; }; - 5A6216FF081F6510212318F7654826F2 /* huffman_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 16D25A3C7A45F1954C4E6B068C58B7EA /* huffman_utils.c */; }; - 5C24BD9E95BD5410843BD372C028EF2F /* cpu.c in Sources */ = {isa = PBXBuildFile; fileRef = A7467F12C852FA55CE696623D0C49893 /* cpu.c */; }; - 5C603F51B177ED3F49DA5B71B25253F4 /* bit_reader_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 143960F19300CE4777F0CC55F8DD8E31 /* bit_reader_utils.c */; }; - 5CC284C9FBACD9B6424307C1E8EE2618 /* PostHog-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B2AAEABCF4BAAC96E0099538E6FAEA5 /* PostHog-dummy.m */; }; - 5E514467B3F4FB8469C113819DE25FF2 /* String+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C872681D85E83B48A177D688728D6E /* String+Util.swift */; }; - 5F8EF5FE4D73E4F038E2DEA4D5C6A146 /* PostHogQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3CF8C4E1C8C5343DE62E4B8A2043AD /* PostHogQueue.swift */; }; - 6045482673A7946DF31DA4DE7CBC35F6 /* PostHogSurvey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84990ADFAD35F17E5D87A70DE6B2768 /* PostHogSurvey.swift */; }; - 615D7CAA8FFEFF59CC50B16C3981EBE4 /* PostHogStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFB8864DE15913957BF2887CC886727E /* PostHogStorageManager.swift */; }; - 627DC06DB4D42F2FB9605AF95A528471 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EBB44BB3ED427636A0A0A673F61914 /* Errors.swift */; }; - 62B8676E90C8E48016693783C0036D8F /* ssim_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 71C01AE5C1556B7FCEB70598A2359C57 /* ssim_sse2.c */; }; - 645BFE97A3A8E993F08B87285951AB82 /* TimeBasedEpochGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74828AFF764C4FBA1E8273C81B8E961D /* TimeBasedEpochGenerator.swift */; }; - 64C0E1A516D383EA3EC23A7BFC448FCE /* EmojiRating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07ACC03496AD0255B5AF70CE5B4D3739 /* EmojiRating.swift */; }; - 65FEDD05D8424912681558FCA1154C7D /* filters_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 262DE51E90B8F78B18D4D167AAC6C4B4 /* filters_utils.c */; }; - 6899E938FC1259735FECEDCCBD5DCB12 /* muxinternal.c in Sources */ = {isa = PBXBuildFile; fileRef = 58890CE1C13F1D218F91B8F1B662297C /* muxinternal.c */; }; - 6979FEA56C2EE86990E4473D5F0085CF /* ApplicationLifecyclePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F788308CE59B5B07BDA5820491EC32B /* ApplicationLifecyclePublisher.swift */; }; - 6A604351BC7DAE8F47EADA54FA67BA92 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */; }; - 6B8EC9F9C886019474D30A2A38FF68B2 /* ConfirmationMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AC357F49D750FDC096D0A38EF084E0F /* ConfirmationMessage.swift */; }; - 6CCFABEA9067B84D78A97723B7424B2A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */; }; - 6CD6C7D137F96E4B377097CC311E07A9 /* sharpyuv_dsp.c in Sources */ = {isa = PBXBuildFile; fileRef = DB9CEA743E2F233A85546C94DD807354 /* sharpyuv_dsp.c */; }; - 6D1C160E221E65E31F2C2C1FB79BF4B3 /* rescaler_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 85583DB1C0D12DF398032FE155609A9E /* rescaler_utils.c */; }; - 6DCDBF153AE370C69768E7A7B57F0748 /* PostHogSurvey+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 752389685141ECE7C426B3DEB3D9F601 /* PostHogSurvey+Display.swift */; }; - 7203FB71B522DF771D5BF42CFE86CE87 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 24177CAF8EE40225DC590E24E98B5907 /* utils.c */; }; - 7206DFEB5BB955754C39FC17011FF545 /* PostHogSurveyIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B69967DBE2F79C655769D9E766052B9 /* PostHogSurveyIntegration.swift */; }; - 7241253A695D3360662518FABF8A18F9 /* Pods-Cable-CableUITests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C77B0E69679F739AA5F6EF3472F81B3E /* Pods-Cable-CableUITests-dummy.m */; }; - 73FCA47CCEAFE7E4B070B112F21A9C7E /* PostHogSurveyEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = 701958AB5C27BF0CCC2736BED2525ADA /* PostHogSurveyEnums.swift */; }; - 7460FA92770379998EFE3064CC0A9530 /* ph_sharpyuv.h in Headers */ = {isa = PBXBuildFile; fileRef = E71BC82ADF0D9EC4F9A390C9D17DAEB7 /* ph_sharpyuv.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 74A2933F21E1826BFCE98767611A1F2B /* PostHogAutocaptureIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3B1285FF54F13BB960970B7370E7941 /* PostHogAutocaptureIntegration.swift */; }; - 74B9900ECC81F957E50D152BBBED824E /* NetworkSample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516914075585022AAE4D7BBA974FC678 /* NetworkSample.swift */; }; - 7597FE04F73E19981054D053749CBC7C /* picture_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 856534F765DD23D7992D3EC872FE93B7 /* picture_enc.c */; }; - 76A22346D2892A75D2C605B1E46C9C01 /* ph_encode.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A40B8F5231A7944996ACCC1E340899D /* ph_encode.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 76A53A0EB8A7F9C73557B3B32D669BBB /* PostHogSwiftUIViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D50B5F78458141D8272AA365385479 /* PostHogSwiftUIViewModifiers.swift */; }; - 77EB7B90D005993B65D9AD7A2533B13D /* enc_sse41.c in Sources */ = {isa = PBXBuildFile; fileRef = 399AFF4367999AB195B1A4861DE2E3E1 /* enc_sse41.c */; }; - 790E4BF42B672B3EA4E21B66CB0DB05B /* lossless_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 9E7A2B7242409A2E9C0F0B5B9DBCC3A8 /* lossless_sse2.c */; }; - 7A4857DA3F9C646CCC878F9329D19E60 /* Pods-CableTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D07D529105A58FA284174316608207E1 /* Pods-CableTests-dummy.m */; }; - 7DA49ED9EF36CCDB27E476FFFC6D5660 /* PostHogFileBackedQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 866330A74D6DEBA7354A80F57B4FB378 /* PostHogFileBackedQueue.swift */; }; - 7E015D3260F0BFD29E3F0690B1E59E0D /* ApplicationViewLayoutPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60B359AA89658785332F5EB04C61112 /* ApplicationViewLayoutPublisher.swift */; }; - 7FDBA2F04F5750B61E6DF8FA25F849BA /* iterator_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4282C330B006663023D6DA60644E9C71 /* iterator_enc.c */; }; - 80DA5FB9AFF890ABE611B5A044CDB036 /* rescaler.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DE0CB3567095D87C771243576EF9353 /* rescaler.c */; }; - 81A20054B797FF0F766333BE4913DBCC /* alpha_processing_sse41.c in Sources */ = {isa = PBXBuildFile; fileRef = 676CFD3B35C9F8564C51E1B617A3937C /* alpha_processing_sse41.c */; }; - 81A74112BDB883335465416F2DDD4F73 /* PostHogSessionReplayConsoleLogsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA18A14AB5E18A4522909D739010F8B /* PostHogSessionReplayConsoleLogsPlugin.swift */; }; - 822476AFB7FFB4F8605DC4FA8A19D926 /* PostHogSwizzler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AC865B00E170495D08F65246F26683 /* PostHogSwizzler.swift */; }; - 82AAEEC1837B795CD013636BAEF14DA9 /* yuv_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = B233AD5FB3C464116639B4C4A446D5A9 /* yuv_sse2.c */; }; - 83FEE6E290BF6CA38102DFB7009F6E4B /* ph_lossless_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 44B2D98D590BDDF72420B7AC9EE530EF /* ph_lossless_common.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 847080B8D7683BBBEF894EE6EA38912E /* PostHogSurveysConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D68B2FA6D4A041D8698FC67954D8CF /* PostHogSurveysConfig.swift */; }; - 8494528FA0360A1CAFFAC147C9849375 /* ApplicationScreenViewPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B5BAECFF77239E331B522C7107C1A0 /* ApplicationScreenViewPublisher.swift */; }; - 858173DFD0E9446CB773D1CBB00418BD /* dec_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = 0093492C1008E66439CF39ACE752BD1B /* dec_neon.c */; }; - 85A31731349118825F11B6BD97570A72 /* Pods-Cable-CableUITestsScreenshot-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C94C1436FB9990B1C8458A4890A74FDD /* Pods-Cable-CableUITestsScreenshot-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 85E00607DEA0B702A88256B889A0AE46 /* ph_color_cache_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 10FE6C33488F83B12D9A37C71584E723 /* ph_color_cache_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 866CAB0B3150FEF773928C86AD9171E1 /* ViewTreeSnapshotStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA1A0EEEA8915D7ED932898580D55DA /* ViewTreeSnapshotStatus.swift */; }; - 8753E838E3006A1E2F72DF5F6AC2C67A /* Date+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07439B205D54D4542249407BDC340D8B /* Date+Util.swift */; }; - 889631E71D504EF98E0F26B2E5E45DD9 /* MultipleChoiceOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF7956F6F3924F58644A57ECA19C3559 /* MultipleChoiceOptions.swift */; }; - 89AB8E555A39C1D168505C89C8CF5777 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A884B1BB87102903507CF0084AE903C /* UIViewController.swift */; }; - 89D364405A06C6C8B06DA285AC3E5A5F /* filter_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8C1E609130982209FE02FE99B78BDC88 /* filter_enc.c */; }; - 8A2EA1FD1DE278407880880388CC7863 /* thread_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = BCE3D06D9F5BD48177BA8DD707F3B037 /* thread_utils.c */; }; - 8A7B3562B53BEF18CBED9C9821C6A371 /* ph_format_constants.h in Headers */ = {isa = PBXBuildFile; fileRef = FB3C8E706E1BB31142E0F7F3BDAAD7A3 /* ph_format_constants.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8B95C63A2E2B72090AE5A5AB08AA2F26 /* ForwardingPickerViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BC3CBC81194A98EBD7798839CE8D8C4 /* ForwardingPickerViewDelegate.swift */; }; - 8CC38A2ED3E279B2AFF414782AF6EF05 /* PostHogSessionReplayNetworkPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C45951D83FB2A2320DCC090D6EE882 /* PostHogSessionReplayNetworkPlugin.swift */; }; - 8D7AFBBCADCA7780494700C5EE031A7A /* SurveysWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A8771BEB422C0EBA1669CCBCC77482 /* SurveysWindow.swift */; }; - 8E6CE9ACB1DEA1CB2ED4E8C43A6CB1DF /* picture_psnr_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = FD4EE07A95F242B51F29985FC791B86C /* picture_psnr_enc.c */; }; - 8EC01D8A4D04C123025FD6958165F4F5 /* ph_quant_levels_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C5591E49085B223152BC20087DBC2F5 /* ph_quant_levels_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 900853FFC431D50BA4A0E6B015873734 /* quant_levels_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8460AF919A0E871BF4A73C742BE5F688 /* quant_levels_utils.c */; }; - 918561BED43D6D9A55D7ED87A4233FAA /* quant_levels_dec_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = D601464067F9E5B1E5D5620C8924912F /* quant_levels_dec_utils.c */; }; - 91A181721A95245C3A19DD7DF8D3ED20 /* quant_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 58B2390554BF82073BFFF56330375E16 /* quant_enc.c */; }; - 923C6E3BE82D64E5623625D81D82FBA8 /* ph_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 81A9CA6BA8912B85E00744D072E0AF98 /* ph_types.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 932C39898E846B7343F58C87A7C8A18D /* upsampling_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 58E3377D17B30CB16D0310778D68FF1B /* upsampling_sse2.c */; }; - 93F25171D356A06B938299A8DC1B01A3 /* PostHogAutocaptureEventTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770559699B6C4F893D1E7A02F2CD0026 /* PostHogAutocaptureEventTracker.swift */; }; - 944E6DAB32560E951BAE16F5157F0D35 /* PostHogNextSurveyQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3261BD495FD61B20BC8BA98578865B7 /* PostHogNextSurveyQuestion.swift */; }; - 947857B02E90224ED7E680233305DB5E /* PostHogSurveyConditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B45CA8B0658CABEFE16DEF11945D4C96 /* PostHogSurveyConditions.swift */; }; - 95D55CBE1ACFF50FD1EBC6D4936B2E94 /* PostHogConsumerPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDB8663680B7CBA9F5E40A296878638 /* PostHogConsumerPayload.swift */; }; - 96B479CE48E29ED5239A38806540AFA6 /* enc.c in Sources */ = {isa = PBXBuildFile; fileRef = B73ECF10580052B7EADF1C8126C1F5D4 /* enc.c */; }; - 9748FF5103EE1045677BDAED5F00DFC9 /* PostHogSessionReplayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 103153A1783959BDADE5A086BAEE932E /* PostHogSessionReplayConfig.swift */; }; - 97F67C732B893F315EFDD5D598C3F71E /* config_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 738AC56CBEF0F5C46F68B5F6C393CE26 /* config_enc.c */; }; - 9ADEA2121D351229E95318A512FB3E0F /* cost.c in Sources */ = {isa = PBXBuildFile; fileRef = B24A16C60C8C5673028974678594E6E8 /* cost.c */; }; - 9B3694B60BA51B86814BC4B1C0AADE57 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E80C814BD248103DB298C992AB07604 /* Reachability.swift */; }; - 9B7B7D98445B36EC45CFE01B99DC85AB /* lossless_enc_sse41.c in Sources */ = {isa = PBXBuildFile; fileRef = EFBDDE4003994362E3FE58381610B087 /* lossless_enc_sse41.c */; }; - 9C0BAA41861364D4FF410FA385BA695D /* NumberRating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B5738CAC80AE5D72B75358EEE9D7559 /* NumberRating.swift */; }; - 9C1AF5AC07E7ED9CCAFECF84C27E73BD /* PostHogPropertiesSanitizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8ACAC7F2DBEC2E6F6C08E461831738E /* PostHogPropertiesSanitizer.swift */; }; - 9C1C48333A30CF28A6F8F918216615FB /* UIColor+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7A62B6B9F7CF772B7553E3B238AAB3 /* UIColor+Util.swift */; }; - 9C95E9E96A19763B1F0B805841F064BF /* ph_histogram_enc.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DA4E5C73A12E0A6898B70F3A9CED1A6 /* ph_histogram_enc.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9DAE58DDF04BB8AC6700C178740CA4A2 /* webp_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 46FD7041B91F02D8C44A8B23724610E8 /* webp_enc.c */; }; - 9DFA8122176106229CC767FEAF4B3304 /* lossless_enc_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = 69AF4F959032087EDA8FC0D0A5B6209D /* lossless_enc_neon.c */; }; - 9E27DE50AA66E7023C06263FB25B71AE /* filters_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 13CD7EE11BBD992CE37375834390CA29 /* filters_sse2.c */; }; - 9E32FD9B51892EE4CDE615D15D2FC878 /* ph_vp8li_dec.h in Headers */ = {isa = PBXBuildFile; fileRef = 229D517849A145DB966B04AE150168FB /* ph_vp8li_dec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9FAB5C450670E109F781D6C53E56D2AF /* PostHog.h in Headers */ = {isa = PBXBuildFile; fileRef = 67DFC8AB8EF46C4E3F28229F5DCE1C8C /* PostHog.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9FF301A6E7FAA2AAE00ACD5E80FD4C9B /* ph_cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 93E56AB9DB0FA2E618B09B988C467DD9 /* ph_cpu.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A086FB83D5610E0968532EF591FDC242 /* ph_mux.h in Headers */ = {isa = PBXBuildFile; fileRef = FC51F4877491548B2CE8612D8EBE8994 /* ph_mux.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A1C65033EA26B7634A850B9635138B45 /* PostHogSurveyQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA884DB8D0FD6DE10EC0E09ECBCB0FCC /* PostHogSurveyQuestion.swift */; }; - A2111B640365DEB1D1262861FB526BB6 /* Data+Gzip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFF0D65E2D3133D28C9527F218B033B /* Data+Gzip.swift */; }; - A23F680D118045C38E24C79BCA87F3F8 /* MethodSwizzler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DCCB7DFFA938AEFD75591B645CDDDF0 /* MethodSwizzler.swift */; }; - A25DB2959F2533A76503BA4FA1C3D8A8 /* ph_bit_writer_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = C15956FAD29CD446AED83B28072F258A /* ph_bit_writer_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A4351828FD656D27899119C43282A46A /* Pods-Cable-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B075A62E32AE782E63CB647C2425908 /* Pods-Cable-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A49FA4C1772B8C68D42DD34E69C76134 /* lossless_sse41.c in Sources */ = {isa = PBXBuildFile; fileRef = 8F310530386C455D52673B41DA4A2B76 /* lossless_sse41.c */; }; - A7E4B6A307B6D11D3216D24DFF540A76 /* upsampling_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = C52A7E8E1B3DA2B543DA937C3BE7162B /* upsampling_neon.c */; }; - A87193AFE5CEF7AA673BC80BA4FB9C9B /* UIApplication+.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07415FC3293C535290E997DFFF959FD /* UIApplication+.swift */; }; - A881E1170145D6E36C8AD2E62BDCBB9A /* ph_common_dec.h in Headers */ = {isa = PBXBuildFile; fileRef = D22E0F8ACB0AADC33D8564D1D4E72C93 /* ph_common_dec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A923905603728FF1F5D63FA3383A7647 /* upsampling_sse41.c in Sources */ = {isa = PBXBuildFile; fileRef = C87BE5C48779C989CE62FA8282E8B3D5 /* upsampling_sse41.c */; }; - A958EABDF4F3CF594A315F89E9E5CB16 /* PostHogDisplaySurvey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66AFFADA1464DFC165AF775D6EDDE652 /* PostHogDisplaySurvey.swift */; }; - A9947D06E0701770A3D401BD4B4DA0D1 /* ph_muxi.h in Headers */ = {isa = PBXBuildFile; fileRef = E20DB8D1A1DFFDC41590109620887074 /* ph_muxi.h */; settings = {ATTRIBUTES = (Public, ); }; }; - AC8C4D813E979741EB84B24C111118F0 /* PostHogMaskViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88D282FD37C24951D615E881B2D8B5E /* PostHogMaskViewModifier.swift */; }; - ACF234EB88FF3EFA147BF0AB0A317A4D /* ph_vp8_dec.h in Headers */ = {isa = PBXBuildFile; fileRef = AC8C2B6DA50936FEAAA8D7CF5AD97F60 /* ph_vp8_dec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - AD26200A6DEF1516F1C192768115BC55 /* filters.c in Sources */ = {isa = PBXBuildFile; fileRef = 22394C97F8C3BED5DFED7C780C1CE564 /* filters.c */; }; - AF9FF002C9339CCCD33ADA5476D20C72 /* ph_vp8i_dec.h in Headers */ = {isa = PBXBuildFile; fileRef = 63BDD7A067C75995C9F44A5BD44EE5E7 /* ph_vp8i_dec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B1F2CAAAD6999BC7C09AE72FB869D703 /* CGColor+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF0D333280C1AA92C268AF36D289B8B /* CGColor+Util.swift */; }; - B4A5799A36D3D50E81703A45BAEE77A3 /* ph_common_sse2.h in Headers */ = {isa = PBXBuildFile; fileRef = F4C64C6E9AA1FDB5C402603F7B39F3BC /* ph_common_sse2.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B511EA85DAC18FD6205B26B71DE1050C /* UIView+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3D701A5BB86EA2FBA69F57589340667 /* UIView+Util.swift */; }; - B9342CDA79BCBDBEAB3D05BB11EDDBFD /* Pods-Cable-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A2428DC0690BFC906FB6BBC9517C91B8 /* Pods-Cable-dummy.m */; }; - B94F9615E1406645D9976C740A12F52E /* RRWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA36D795E2D2304CF2F39068E99EED8 /* RRWireframe.swift */; }; - B959C06622003BB1462ABA53313D422B /* PostHogSessionReplayConsoleLogConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4A2E54DC65E09C0702AC19D93C51D0 /* PostHogSessionReplayConsoleLogConfig.swift */; }; - BD645B55581F18C45D3A077C7120F169 /* dec_clip_tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 801F6B74D8E565882EB6463915EE29D9 /* dec_clip_tables.c */; }; - BDE90F6901C6138EF6CB8F5ADA8F6061 /* ph_thread_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 52ABD4159B83070BF140003F701169DE /* ph_thread_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BF00726324688B38AB498AB8C0D6A9CC /* PostHogVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40B6975FA38F82CFAD982D745DC1177 /* PostHogVersion.swift */; }; - BFF5E04D16488F3C122C6F2099D16702 /* PostHogIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58006037934279EEB6F765806CE31D38 /* PostHogIntegration.swift */; }; - C085F172DD546D7A954F9E7CFA6549AF /* Float+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = F091F375187698A52912BC097838826A /* Float+Util.swift */; }; - C10F91F1C306F86A8DE647E9392EAD5B /* PostHogAppLifeCycleIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAA59A93C40F18F756F62E7761DDDA2 /* PostHogAppLifeCycleIntegration.swift */; }; - C1D7ABBD432849EC886D9852B1A06CC4 /* SurveyPresentationDetentsRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE8485816F0A8CEE5E8A00890CA324A /* SurveyPresentationDetentsRepresentable.swift */; }; - C20E16F51EE280913ECD5C8939075863 /* picture_tools_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = CDACF4D658D473E1D0C0BC475C6B6D9F /* picture_tools_enc.c */; }; - C2D9AAF9E55A505AAEA9DDC14B0BED48 /* color_cache_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = DF049858415B4235BCDBB079C3581BA4 /* color_cache_utils.c */; }; - C38E5112CB2F46733703F641350364B7 /* enc_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = E7F5A16999097A6591248430D5EE1DD5 /* enc_sse2.c */; }; - C3B88263FFF28B2C4B1C34BBFA2487CB /* ph_backward_references_enc.h in Headers */ = {isa = PBXBuildFile; fileRef = 80A1861E13FC4397C27E11FB9264C784 /* ph_backward_references_enc.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C40C37140BA4AC6DCF085AF14D6CBF2A /* ph_mux_types.h in Headers */ = {isa = PBXBuildFile; fileRef = E851D31270CCB1CA24F49C52015B7F59 /* ph_mux_types.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C41DEDAF518B62C012E82A92168E2F71 /* sharpyuv_gamma.c in Sources */ = {isa = PBXBuildFile; fileRef = ECD1B7D205F7FC63D45533FC2BAE3410 /* sharpyuv_gamma.c */; }; - C4A5285EB3B6FA8347156384E0693E4F /* ph_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = AFC508491302293623F9C9FE123DF31D /* ph_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C8B7CAFDAF0B31D48040478F71C0EBE2 /* cost_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 02AE2A2A0467EA70DB5D9C6CB93B7CBE /* cost_enc.c */; }; - C9FDB67918170E2DD508541BA216560E /* sharpyuv_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = CD9B87A72D9E03E8531BE4E64C96BBAA /* sharpyuv_sse2.c */; }; - CA4046A0C463B283FDEF14E88E615622 /* Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30358A66B1E325E7BF8FACB77136DCBD /* Resources.swift */; }; - CA716D34DD2DC32B06C1C0B0B2C3C5C2 /* ph_filters_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 450735A51B25E2BC879F80FE3EF683E2 /* ph_filters_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CCEB5CA997D8A5E48BD7E73B6F4EB71D /* ssim.c in Sources */ = {isa = PBXBuildFile; fileRef = 06902488BC3ACDB66C4CC8E3DA9B2BF1 /* ssim.c */; }; - CDCFCFCE3B5007033F6B1E3AD1C1545E /* DictUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881B0B1AA8FD87B6D79DB655742B357B /* DictUtils.swift */; }; - CE53F772FBCCABD2B04587B2BD4FD177 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13289E1D4EAA79792BF87E290976C758 /* FileUtils.swift */; }; - CEEA0DE4B861387A64E05F48B5A7CEE5 /* yuv.c in Sources */ = {isa = PBXBuildFile; fileRef = CBF4E22F61B73B539ABF63E8ADAAB87E /* yuv.c */; }; - D16B60C05E3B60104BB3BD49495D0879 /* histogram_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = A9410C5F8607F2FC658BCBEDAD5EEFBE /* histogram_enc.c */; }; - D246F10610362DEE01F847DA6180E08C /* filters_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = 86706A03E7D1636BA28748C6D1F0740A /* filters_neon.c */; }; - D4A6686C68814AF2106A2CAB329530DB /* muxread.c in Sources */ = {isa = PBXBuildFile; fileRef = 42AC22285670786E35A95F104334D436 /* muxread.c */; }; - D4AB0395E8CB9D1D7677DAE14B6B5F59 /* SurveyDisplayController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635FCA4635E4DDDA1A3FC3953FC4C9E9 /* SurveyDisplayController.swift */; }; - D5AC0B15E162B74F89BE5C08EAD54621 /* URLSessionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 905D11210787EACE6D48E66560631096 /* URLSessionExtension.swift */; }; - D76EBEDD6E19898C194F1FADF177664C /* alpha_processing.c in Sources */ = {isa = PBXBuildFile; fileRef = F45BEC0B42FB9EFA2C1B6AADA6AAF9E4 /* alpha_processing.c */; }; - D9BF8B39F7A90185A2AE69B33D1F91C2 /* SwiftUI+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28E5B198F689C1C063C1DE0BF9B7ABAF /* SwiftUI+Util.swift */; }; - DA6006155C3D0397BC53C971B6764235 /* analysis_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = DDC359F157B7B3B8BA8CEDD5389D7EC4 /* analysis_enc.c */; }; - DF22F1DD5882402BECE6B91CD1A227E8 /* SurveysRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF87321A55BD1044FFD6A604445B369 /* SurveysRootView.swift */; }; - DFA1A5698507154A30CAA31FD6BD1A85 /* alpha_processing_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 795E63F5459422366F9C7A22AEE66832 /* alpha_processing_sse2.c */; }; - E0749957A6BB7594DC26469DB77B0C7B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */; }; - E1A545A284C243E666880CB425BA9F20 /* ph_vp8i_enc.h in Headers */ = {isa = PBXBuildFile; fileRef = 27378C145AF70BB39C853D63F329CD6D /* ph_vp8i_enc.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E2A195E40A0C3853004F3B1C7E6ECD1C /* ph_quant.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F3CFCC8A376939FB12B819B27393F00 /* ph_quant.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E2BB8174AB27B7FC6D610F3D6D2E55C9 /* PostHogDisplaySurveyAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F211460A3349D6478176319237385E /* PostHogDisplaySurveyAppearance.swift */; }; - E317933C3FAD82DD8D0C65EA369EF44A /* ph_dsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BFDFC30C1DB0FBE9E1AAB4CA5D5E990 /* ph_dsp.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E3C0C764F23ABB7818F31999FE8856BF /* SegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB852437A4FE639A36D1C941F94F307A /* SegmentedControl.swift */; }; - E617B7F0DBC07F3490AE7811551911D5 /* ph_rescaler_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = C95684E80F2639489418BE63DB2999CD /* ph_rescaler_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E6285A5C5201781908B0FA8CC012CF82 /* ph_endian_inl_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 9F7478CDF58D2E47C91E9AEA4F3BFA36 /* ph_endian_inl_utils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E641EDF95A76D284807990177906C9C7 /* frame_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = ED013EDE4144AF0F72EA8632E927919E /* frame_enc.c */; }; - E645C1A88AD4148D5B873D3811EA3BE0 /* QuestionTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C816478A7A89278F0B9617CD33B4ABB /* QuestionTypes.swift */; }; - E68C5CF478D5211B525ABCD7A11F346A /* PostHogLogEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F6F40CA78AF34A91BC6DEC8595DF55 /* PostHogLogEntry.swift */; }; - E6E78E2A322D4E7736D55ADCE1D73BF6 /* syntax_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 390E5F5970C7EFFE8A499DFEFE839B86 /* syntax_enc.c */; }; - E7475B260DBC82214488EF1C5712B807 /* ph_sharpyuv_csp.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E3A1E4CB22BAA257379DDFFB6654CC3 /* ph_sharpyuv_csp.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E9576BFC34B3253843CC4778948ADC07 /* ph_yuv.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F05DC32B52DAD2AA8964AB408980A1F /* ph_yuv.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E9F821F9B181137CF9756E7B5DB290F8 /* PostHogSurveyAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB66D01B3E08701E5AD477ECC306070 /* PostHogSurveyAppearance.swift */; }; - EAEEFEB5F2F8EDC54B52B4FEE8E0021E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */; }; - EB51376036E66B13F5DA94861714AF5A /* ph_lossless.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ABF4049707FD7850C0863E5B99FC269 /* ph_lossless.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EB6484265BE63031CDDCB790CD983769 /* lossless_enc_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 53A2AF1A2EBA24407AC0E0E425DED2AB /* lossless_enc_sse2.c */; }; - EBDF53B9DD3C2058CE110560E30904D6 /* vp8l_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 1900B13AA3B133ACDED32587C775ABA2 /* vp8l_enc.c */; }; - EC0A7D763F31745069FD68B9549C9A9A /* dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 9DC16A20D6C39A6FE6C700DE88890FC6 /* dec.c */; }; - EF9115BBB1E249A72211587F17B96835 /* muxedit.c in Sources */ = {isa = PBXBuildFile; fileRef = 172A611FED667B809856910CFB1F8DDA /* muxedit.c */; }; - F149A1F7278934A1B4C094EBF060D2B6 /* PostHogEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8213698CA3EB61F4236A6C8E334099A /* PostHogEvent.swift */; }; - F395CAD4C36A94E525379C352F11ADF4 /* cost_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = F85D4CA294CDFB70EE341652BCF427BC /* cost_sse2.c */; }; - F632525798590D31F88730CB5B08A136 /* backward_references_cost_enc.c in Sources */ = {isa = PBXBuildFile; fileRef = 716805E0DE609F449A51C263A653D182 /* backward_references_cost_enc.c */; }; - F64FD409AECB0B56E3D10EC2B87A36EE /* PostHogLegacyQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6E0620A6F6472F44291ECF29967CCB /* PostHogLegacyQueue.swift */; }; - F8222B205BE0E6FCCBE3ACE42940599A /* cost_neon.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BBA09ECA091215738C1BE2FFE5F091C /* cost_neon.c */; }; - FDDD950364CB85A70790993AD79EFB17 /* PostHogNoMaskViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 905362F80FAC60800B3E20073D217B0C /* PostHogNoMaskViewModifier.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 0D8CAE93284D0305C41A4E72F6A07403 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8879D5F28A55518ACFB247594F87F75A; - remoteInfo = PostHog; - }; - 61238D0FC731DB2BBAA0E8FC812EACC8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8012054959C338A62834AD9706977FB0; - remoteInfo = "Pods-Cable"; - }; - 62E12AA42617D0B8EC3B5959449CF6A7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8879D5F28A55518ACFB247594F87F75A; - remoteInfo = PostHog; - }; - BF702A62A6C910D491628C2736B02269 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8879D5F28A55518ACFB247594F87F75A; - remoteInfo = PostHog; - }; - F71674A1D40932D753945F87EC1C5BB1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = E326EE08AE4CF9FA8C947B96B6F8AB07; - remoteInfo = "PostHog-PostHog"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 0093492C1008E66439CF39ACE752BD1B /* dec_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = dec_neon.c; path = vendor/libwebp/dec_neon.c; sourceTree = ""; }; - 00B098547C4C7CC1269E78AD6477793A /* Pods-Cable-CableUITestsScreenshot.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Cable-CableUITestsScreenshot.modulemap"; sourceTree = ""; }; - 02AE2A2A0467EA70DB5D9C6CB93B7CBE /* cost_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = cost_enc.c; path = vendor/libwebp/cost_enc.c; sourceTree = ""; }; - 02F6988AFBC0B2AF511F86755F935B13 /* alpha_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = alpha_enc.c; path = vendor/libwebp/alpha_enc.c; sourceTree = ""; }; - 03F6F40CA78AF34A91BC6DEC8595DF55 /* PostHogLogEntry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogLogEntry.swift; path = "PostHog/Replay/Plugins/Console Logs/PostHogLogEntry.swift"; sourceTree = ""; }; - 04F316A4B2CB95903F1476FD8F6E5506 /* token_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = token_enc.c; path = vendor/libwebp/token_enc.c; sourceTree = ""; }; - 05688B486C05767CC332F7BF18F63FA6 /* PostHogRemoteConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogRemoteConfig.swift; path = PostHog/PostHogRemoteConfig.swift; sourceTree = ""; }; - 06902488BC3ACDB66C4CC8E3DA9B2BF1 /* ssim.c */ = {isa = PBXFileReference; includeInIndex = 1; name = ssim.c; path = vendor/libwebp/ssim.c; sourceTree = ""; }; - 07439B205D54D4542249407BDC340D8B /* Date+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Date+Util.swift"; path = "PostHog/Replay/Date+Util.swift"; sourceTree = ""; }; - 07ACC03496AD0255B5AF70CE5B4D3739 /* EmojiRating.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EmojiRating.swift; path = PostHog/Surveys/Utils/EmojiRating.swift; sourceTree = ""; }; - 0B5738CAC80AE5D72B75358EEE9D7559 /* NumberRating.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NumberRating.swift; path = PostHog/Surveys/Utils/NumberRating.swift; sourceTree = ""; }; - 0B79B981CBC521AEB9B5F19CEE6486D8 /* Pods-Cable-CableUITests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Cable-CableUITests-acknowledgements.plist"; sourceTree = ""; }; - 0C5591E49085B223152BC20087DBC2F5 /* ph_quant_levels_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_quant_levels_utils.h; path = vendor/libwebp/ph_quant_levels_utils.h; sourceTree = ""; }; - 0CFF0D65E2D3133D28C9527F218B033B /* Data+Gzip.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+Gzip.swift"; path = "PostHog/Utils/Data+Gzip.swift"; sourceTree = ""; }; - 0E6E0620A6F6472F44291ECF29967CCB /* PostHogLegacyQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogLegacyQueue.swift; path = PostHog/PostHogLegacyQueue.swift; sourceTree = ""; }; - 103153A1783959BDADE5A086BAEE932E /* PostHogSessionReplayConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSessionReplayConfig.swift; path = PostHog/Replay/PostHogSessionReplayConfig.swift; sourceTree = ""; }; - 10FE6C33488F83B12D9A37C71584E723 /* ph_color_cache_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_color_cache_utils.h; path = vendor/libwebp/ph_color_cache_utils.h; sourceTree = ""; }; - 114279FBFB8CEFB0597BCACAFD61F21C /* ph_bit_reader_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_bit_reader_utils.h; path = vendor/libwebp/ph_bit_reader_utils.h; sourceTree = ""; }; - 13289E1D4EAA79792BF87E290976C758 /* FileUtils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FileUtils.swift; path = PostHog/Utils/FileUtils.swift; sourceTree = ""; }; - 13CD7EE11BBD992CE37375834390CA29 /* filters_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = filters_sse2.c; path = vendor/libwebp/filters_sse2.c; sourceTree = ""; }; - 143960F19300CE4777F0CC55F8DD8E31 /* bit_reader_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = bit_reader_utils.c; path = vendor/libwebp/bit_reader_utils.c; sourceTree = ""; }; - 15861BEA1F90094B56DC2CD840469B3D /* AssociatedKeys.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssociatedKeys.swift; path = PostHog/Utils/AssociatedKeys.swift; sourceTree = ""; }; - 15D68B2FA6D4A041D8698FC67954D8CF /* PostHogSurveysConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurveysConfig.swift; path = PostHog/Surveys/PostHogSurveysConfig.swift; sourceTree = ""; }; - 1615B66B1E3F08A9CCE5056265CA80A8 /* PostHog.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = PostHog.modulemap; sourceTree = ""; }; - 16D25A3C7A45F1954C4E6B068C58B7EA /* huffman_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = huffman_utils.c; path = vendor/libwebp/huffman_utils.c; sourceTree = ""; }; - 172A611FED667B809856910CFB1F8DDA /* muxedit.c */ = {isa = PBXFileReference; includeInIndex = 1; name = muxedit.c; path = vendor/libwebp/muxedit.c; sourceTree = ""; }; - 1900B13AA3B133ACDED32587C775ABA2 /* vp8l_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = vp8l_enc.c; path = vendor/libwebp/vp8l_enc.c; sourceTree = ""; }; - 1A884B1BB87102903507CF0084AE903C /* UIViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIViewController.swift; path = PostHog/UIViewController.swift; sourceTree = ""; }; - 1B3EF635F2042C38745AADD3B049F9D1 /* dec_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = dec_sse2.c; path = vendor/libwebp/dec_sse2.c; sourceTree = ""; }; - 1B69967DBE2F79C655769D9E766052B9 /* PostHogSurveyIntegration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurveyIntegration.swift; path = PostHog/Surveys/PostHogSurveyIntegration.swift; sourceTree = ""; }; - 1B7E943BABB7F3EAC5DE3E570F21D845 /* PostHogStorage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogStorage.swift; path = PostHog/PostHogStorage.swift; sourceTree = ""; }; - 1BC3CBC81194A98EBD7798839CE8D8C4 /* ForwardingPickerViewDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ForwardingPickerViewDelegate.swift; path = PostHog/Autocapture/ForwardingPickerViewDelegate.swift; sourceTree = ""; }; - 1CACEF186F8DF80FAFD17F7D33BC5D71 /* URLSessionSwizzler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLSessionSwizzler.swift; path = PostHog/Replay/Plugins/Network/URLSessionSwizzler.swift; sourceTree = ""; }; - 1DA4E5C73A12E0A6898B70F3A9CED1A6 /* ph_histogram_enc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_histogram_enc.h; path = vendor/libwebp/ph_histogram_enc.h; sourceTree = ""; }; - 1DB59137CCCF0AB605EA3C16F30B47C7 /* UITextInputTraits+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITextInputTraits+Util.swift"; path = "PostHog/Replay/UITextInputTraits+Util.swift"; sourceTree = ""; }; - 1E3A1E4CB22BAA257379DDFFB6654CC3 /* ph_sharpyuv_csp.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_sharpyuv_csp.h; path = vendor/libwebp/ph_sharpyuv_csp.h; sourceTree = ""; }; - 1F788308CE59B5B07BDA5820491EC32B /* ApplicationLifecyclePublisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ApplicationLifecyclePublisher.swift; path = "PostHog/App Life Cycle/ApplicationLifecyclePublisher.swift"; sourceTree = ""; }; - 1F97F6C297E221C6D17A28FD64FD56C9 /* ReadWriteLock.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReadWriteLock.swift; path = PostHog/Utils/ReadWriteLock.swift; sourceTree = ""; }; - 1FDB8663680B7CBA9F5E40A296878638 /* PostHogConsumerPayload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogConsumerPayload.swift; path = PostHog/PostHogConsumerPayload.swift; sourceTree = ""; }; - 22394C97F8C3BED5DFED7C780C1CE564 /* filters.c */ = {isa = PBXFileReference; includeInIndex = 1; name = filters.c; path = vendor/libwebp/filters.c; sourceTree = ""; }; - 229D517849A145DB966B04AE150168FB /* ph_vp8li_dec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_vp8li_dec.h; path = vendor/libwebp/ph_vp8li_dec.h; sourceTree = ""; }; - 235F09390CCC154EF3CE1AF84501B9D5 /* yuv_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = yuv_neon.c; path = vendor/libwebp/yuv_neon.c; sourceTree = ""; }; - 24177CAF8EE40225DC590E24E98B5907 /* utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = utils.c; path = vendor/libwebp/utils.c; sourceTree = ""; }; - 262A25E1DBCEB2502F06974ED0FE5FBD /* SurveyButton.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SurveyButton.swift; path = PostHog/Surveys/Utils/SurveyButton.swift; sourceTree = ""; }; - 262DE51E90B8F78B18D4D167AAC6C4B4 /* filters_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = filters_utils.c; path = vendor/libwebp/filters_utils.c; sourceTree = ""; }; - 267A77D966A9CB080522F80053CB2597 /* PostHogTagViewModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogTagViewModifier.swift; path = PostHog/SwiftUI/PostHogTagViewModifier.swift; sourceTree = ""; }; - 268E4C94970868F0267B55DD268C4FDC /* ph_vp8li_enc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_vp8li_enc.h; path = vendor/libwebp/ph_vp8li_enc.h; sourceTree = ""; }; - 26FE4D61E0E5E088E0A627B0FDFC294E /* Survey+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Survey+Util.swift"; path = "PostHog/Surveys/Utils/Survey+Util.swift"; sourceTree = ""; }; - 2735D5533C9516CBB656FC0E44B60D7B /* Pods-Cable-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Cable-frameworks.sh"; sourceTree = ""; }; - 27378C145AF70BB39C853D63F329CD6D /* ph_vp8i_enc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_vp8i_enc.h; path = vendor/libwebp/ph_vp8i_enc.h; sourceTree = ""; }; - 276E633F3287EC40CFDCCA704F85A7BB /* random_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = random_utils.c; path = vendor/libwebp/random_utils.c; sourceTree = ""; }; - 28E5B198F689C1C063C1DE0BF9B7ABAF /* SwiftUI+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SwiftUI+Util.swift"; path = "PostHog/Surveys/Utils/SwiftUI+Util.swift"; sourceTree = ""; }; - 2AB66D01B3E08701E5AD477ECC306070 /* PostHogSurveyAppearance.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurveyAppearance.swift; path = PostHog/Models/Surveys/PostHogSurveyAppearance.swift; sourceTree = ""; }; - 2D78E84DEDA315BA0155AF3658637CFC /* huffman_encode_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = huffman_encode_utils.c; path = vendor/libwebp/huffman_encode_utils.c; sourceTree = ""; }; - 2DE0CB3567095D87C771243576EF9353 /* rescaler.c */ = {isa = PBXFileReference; includeInIndex = 1; name = rescaler.c; path = vendor/libwebp/rescaler.c; sourceTree = ""; }; - 30238EC8C0FAB5B9682951C8C40A56BC /* yuv_sse41.c */ = {isa = PBXFileReference; includeInIndex = 1; name = yuv_sse41.c; path = vendor/libwebp/yuv_sse41.c; sourceTree = ""; }; - 30358A66B1E325E7BF8FACB77136DCBD /* Resources.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Resources.swift; path = PostHog/Surveys/Utils/Resources.swift; sourceTree = ""; }; - 34EBB44BB3ED427636A0A0A673F61914 /* Errors.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Errors.swift; path = PostHog/Utils/Errors.swift; sourceTree = ""; }; - 36A60AA62169A7D58D9D78E917A84BD6 /* PostHog */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = PostHog; path = PostHog.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 36EBA9BB4949682419A03F0FE285AFDD /* PostHogReplayIntegration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogReplayIntegration.swift; path = PostHog/Replay/PostHogReplayIntegration.swift; sourceTree = ""; }; - 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - 390E5F5970C7EFFE8A499DFEFE839B86 /* syntax_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = syntax_enc.c; path = vendor/libwebp/syntax_enc.c; sourceTree = ""; }; - 394497CE62FB8F5E127D55A66D718803 /* UIView+PostHogLabel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIView+PostHogLabel.swift"; path = "PostHog/Autocapture/UIView+PostHogLabel.swift"; sourceTree = ""; }; - 39451BDDC73A8B6E24FFE97D8F418255 /* UUIDUtils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UUIDUtils.swift; path = PostHog/Utils/UUIDUtils.swift; sourceTree = ""; }; - 399AFF4367999AB195B1A4861DE2E3E1 /* enc_sse41.c */ = {isa = PBXFileReference; includeInIndex = 1; name = enc_sse41.c; path = vendor/libwebp/enc_sse41.c; sourceTree = ""; }; - 39C96D15D41A39D9CA442FE17E0C0C24 /* Pods-Cable-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Cable-acknowledgements.plist"; sourceTree = ""; }; - 3AE65FACEDE1F85D4627DDF23A372ED1 /* Pods-CableTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-CableTests-Info.plist"; sourceTree = ""; }; - 3B075A62E32AE782E63CB647C2425908 /* Pods-Cable-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Cable-umbrella.h"; sourceTree = ""; }; - 3B8A9E04574F84317E10956DD24EE006 /* ph_common_sse41.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_common_sse41.h; path = vendor/libwebp/ph_common_sse41.h; sourceTree = ""; }; - 3BBA09ECA091215738C1BE2FFE5F091C /* cost_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = cost_neon.c; path = vendor/libwebp/cost_neon.c; sourceTree = ""; }; - 3C816478A7A89278F0B9617CD33B4ABB /* QuestionTypes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = QuestionTypes.swift; path = PostHog/Surveys/QuestionTypes.swift; sourceTree = ""; }; - 3EA95D0670D5D47F7BC69002970C0E15 /* rescaler_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = rescaler_sse2.c; path = vendor/libwebp/rescaler_sse2.c; sourceTree = ""; }; - 400531B7575C215F76B0965A3F16DDA0 /* ApplicationEventPublisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ApplicationEventPublisher.swift; path = PostHog/Replay/ApplicationEventPublisher.swift; sourceTree = ""; }; - 4282C330B006663023D6DA60644E9C71 /* iterator_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = iterator_enc.c; path = vendor/libwebp/iterator_enc.c; sourceTree = ""; }; - 42AC22285670786E35A95F104334D436 /* muxread.c */ = {isa = PBXFileReference; includeInIndex = 1; name = muxread.c; path = vendor/libwebp/muxread.c; sourceTree = ""; }; - 42D87DCAF52175DC2D5C6B44419636A8 /* PostHog-PostHog */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "PostHog-PostHog"; path = PostHog.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; - 439F6F8B57A9D0DC1E4C4669A0B6BD70 /* URLSessionInterceptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLSessionInterceptor.swift; path = PostHog/Replay/Plugins/Network/URLSessionInterceptor.swift; sourceTree = ""; }; - 44A2B199B29D72CB41D7DCC66EE77960 /* ph_webpi_dec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_webpi_dec.h; path = vendor/libwebp/ph_webpi_dec.h; sourceTree = ""; }; - 44B2D98D590BDDF72420B7AC9EE530EF /* ph_lossless_common.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_lossless_common.h; path = vendor/libwebp/ph_lossless_common.h; sourceTree = ""; }; - 450735A51B25E2BC879F80FE3EF683E2 /* ph_filters_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_filters_utils.h; path = vendor/libwebp/ph_filters_utils.h; sourceTree = ""; }; - 46FD7041B91F02D8C44A8B23724610E8 /* webp_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = webp_enc.c; path = vendor/libwebp/webp_enc.c; sourceTree = ""; }; - 497BF2253E737EA49DACF8B6460E188A /* UIImage+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIImage+Util.swift"; path = "PostHog/Replay/UIImage+Util.swift"; sourceTree = ""; }; - 49AA24A61F055E7B69C6A5D8C6B2AD78 /* PostHogScreenViewIntegration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogScreenViewIntegration.swift; path = "PostHog/Screen Views/PostHogScreenViewIntegration.swift"; sourceTree = ""; }; - 4AF87321A55BD1044FFD6A604445B369 /* SurveysRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SurveysRootView.swift; path = PostHog/Surveys/SurveysRootView.swift; sourceTree = ""; }; - 4B180FB52B54B3D7CE6A1EB92AF8CD1F /* PostHogSessionReplayPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSessionReplayPlugin.swift; path = PostHog/Replay/Plugins/PostHogSessionReplayPlugin.swift; sourceTree = ""; }; - 4B69F8B2559E5EE459C85C21EFEE836B /* palette.c */ = {isa = PBXFileReference; includeInIndex = 1; name = palette.c; path = vendor/libwebp/palette.c; sourceTree = ""; }; - 4BFDFC30C1DB0FBE9E1AAB4CA5D5E990 /* ph_dsp.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_dsp.h; path = vendor/libwebp/ph_dsp.h; sourceTree = ""; }; - 5131F1FFA6BB84612F7867F86C51FCBE /* UIImage+WebP.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIImage+WebP.swift"; path = "PostHog/Utils/UIImage+WebP.swift"; sourceTree = ""; }; - 516914075585022AAE4D7BBA974FC678 /* NetworkSample.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkSample.swift; path = PostHog/Replay/NetworkSample.swift; sourceTree = ""; }; - 51F57C7D4BAC17BBF066E3F6D4C4B59A /* Pods-Cable-CableUITests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Cable-CableUITests-umbrella.h"; sourceTree = ""; }; - 52ABD4159B83070BF140003F701169DE /* ph_thread_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_thread_utils.h; path = vendor/libwebp/ph_thread_utils.h; sourceTree = ""; }; - 52C45951D83FB2A2320DCC090D6EE882 /* PostHogSessionReplayNetworkPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSessionReplayNetworkPlugin.swift; path = PostHog/Replay/Plugins/Network/PostHogSessionReplayNetworkPlugin.swift; sourceTree = ""; }; - 53A2AF1A2EBA24407AC0E0E425DED2AB /* lossless_enc_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = lossless_enc_sse2.c; path = vendor/libwebp/lossless_enc_sse2.c; sourceTree = ""; }; - 54A7FFEE9A3DE4A9543516D400145E56 /* Pods-CableTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-CableTests.release.xcconfig"; sourceTree = ""; }; - 54D1B502E8A8D8A7CF0F160485F27D94 /* UIWindow+.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIWindow+.swift"; path = "PostHog/Utils/UIWindow+.swift"; sourceTree = ""; }; - 5633C72981EE1D3DFED81A8579AA5217 /* Pods-CableTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-CableTests-acknowledgements.plist"; sourceTree = ""; }; - 56F211460A3349D6478176319237385E /* PostHogDisplaySurveyAppearance.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogDisplaySurveyAppearance.swift; path = PostHog/Surveys/Models/PostHogDisplaySurveyAppearance.swift; sourceTree = ""; }; - 5757A0552803FBE7A020E16BD23EF91B /* Pods-Cable-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Cable-Info.plist"; sourceTree = ""; }; - 57AED642173AA14A5AB8433E3D4B85ED /* PostHogSurveysDefaultDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurveysDefaultDelegate.swift; path = PostHog/Surveys/PostHogSurveysDefaultDelegate.swift; sourceTree = ""; }; - 58006037934279EEB6F765806CE31D38 /* PostHogIntegration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogIntegration.swift; path = PostHog/PostHogIntegration.swift; sourceTree = ""; }; - 58890CE1C13F1D218F91B8F1B662297C /* muxinternal.c */ = {isa = PBXFileReference; includeInIndex = 1; name = muxinternal.c; path = vendor/libwebp/muxinternal.c; sourceTree = ""; }; - 58B2390554BF82073BFFF56330375E16 /* quant_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = quant_enc.c; path = vendor/libwebp/quant_enc.c; sourceTree = ""; }; - 58E3377D17B30CB16D0310778D68FF1B /* upsampling_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = upsampling_sse2.c; path = vendor/libwebp/upsampling_sse2.c; sourceTree = ""; }; - 5A40B8F5231A7944996ACCC1E340899D /* ph_encode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_encode.h; path = vendor/libwebp/ph_encode.h; sourceTree = ""; }; - 5A8552B95757A4F529060DCD92FA9EA8 /* PostHogApi.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogApi.swift; path = PostHog/PostHogApi.swift; sourceTree = ""; }; - 5A93DEA6097B2F04643E499901EC91A6 /* Pods-Cable-CableUITests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Cable-CableUITests-frameworks.sh"; sourceTree = ""; }; - 5C042A1CBFE280735CE602F869E5E16E /* PostHogConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogConfig.swift; path = PostHog/PostHogConfig.swift; sourceTree = ""; }; - 5C1E2A40BEFB3E27559CF75D3AF598D0 /* upsampling.c */ = {isa = PBXFileReference; includeInIndex = 1; name = upsampling.c; path = vendor/libwebp/upsampling.c; sourceTree = ""; }; - 5DF75AE42F75E0133CA974B0A992AF64 /* Pods-Cable-CableUITests */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-Cable-CableUITests"; path = Pods_Cable_CableUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6293FFF4CAA83AC8938A5AF805A16B4E /* alpha_processing_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = alpha_processing_neon.c; path = vendor/libwebp/alpha_processing_neon.c; sourceTree = ""; }; - 6304E8E5FDED5863B9DCE550E79F2F13 /* SurveySheet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SurveySheet.swift; path = PostHog/Surveys/SurveySheet.swift; sourceTree = ""; }; - 635FCA4635E4DDDA1A3FC3953FC4C9E9 /* SurveyDisplayController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SurveyDisplayController.swift; path = PostHog/Surveys/SurveyDisplayController.swift; sourceTree = ""; }; - 63BDD7A067C75995C9F44A5BD44EE5E7 /* ph_vp8i_dec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_vp8i_dec.h; path = vendor/libwebp/ph_vp8i_dec.h; sourceTree = ""; }; - 64AC4C5BA1754D85126569A476612795 /* Pods-Cable.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Cable.modulemap"; sourceTree = ""; }; - 66AFFADA1464DFC165AF775D6EDDE652 /* PostHogDisplaySurvey.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogDisplaySurvey.swift; path = PostHog/Surveys/Models/PostHogDisplaySurvey.swift; sourceTree = ""; }; - 676CFD3B35C9F8564C51E1B617A3937C /* alpha_processing_sse41.c */ = {isa = PBXFileReference; includeInIndex = 1; name = alpha_processing_sse41.c; path = vendor/libwebp/alpha_processing_sse41.c; sourceTree = ""; }; - 67914AAFEBAC9A2F1F127DE54D715E79 /* Pods-Cable-CableUITests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Cable-CableUITests.modulemap"; sourceTree = ""; }; - 67DFC8AB8EF46C4E3F28229F5DCE1C8C /* PostHog.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PostHog.h; path = PostHog/PostHog.h; sourceTree = ""; }; - 69AC865B00E170495D08F65246F26683 /* PostHogSwizzler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSwizzler.swift; path = PostHog/PostHogSwizzler.swift; sourceTree = ""; }; - 69AF4F959032087EDA8FC0D0A5B6209D /* lossless_enc_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = lossless_enc_neon.c; path = vendor/libwebp/lossless_enc_neon.c; sourceTree = ""; }; - 6ABF4049707FD7850C0863E5B99FC269 /* ph_lossless.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_lossless.h; path = vendor/libwebp/ph_lossless.h; sourceTree = ""; }; - 6C3B8263979EC31683158AC2B8D195C7 /* Hedgelog.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Hedgelog.swift; path = PostHog/Utils/Hedgelog.swift; sourceTree = ""; }; - 6DB3C2029FEE43C9492E48815A749CD8 /* DI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DI.swift; path = PostHog/DI.swift; sourceTree = ""; }; - 6E80C814BD248103DB298C992AB07604 /* Reachability.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Reachability.swift; path = PostHog/Utils/Reachability.swift; sourceTree = ""; }; - 6EC9E8EDC00E76181958B8A45361E2E3 /* RRStyle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RRStyle.swift; path = PostHog/Replay/RRStyle.swift; sourceTree = ""; }; - 6F05DC32B52DAD2AA8964AB408980A1F /* ph_yuv.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_yuv.h; path = vendor/libwebp/ph_yuv.h; sourceTree = ""; }; - 6F5F4E6D26C2B48557ACEBE8D608BC49 /* ResourceBundle-PostHog-PostHog-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-PostHog-PostHog-Info.plist"; sourceTree = ""; }; - 701958AB5C27BF0CCC2736BED2525ADA /* PostHogSurveyEnums.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurveyEnums.swift; path = PostHog/Models/Surveys/PostHogSurveyEnums.swift; sourceTree = ""; }; - 701F2C64F76028BD63C8BC57E763B989 /* Pods-Cable-CableUITestsScreenshot-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Cable-CableUITestsScreenshot-dummy.m"; sourceTree = ""; }; - 716805E0DE609F449A51C263A653D182 /* backward_references_cost_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = backward_references_cost_enc.c; path = vendor/libwebp/backward_references_cost_enc.c; sourceTree = ""; }; - 71C01AE5C1556B7FCEB70598A2359C57 /* ssim_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = ssim_sse2.c; path = vendor/libwebp/ssim_sse2.c; sourceTree = ""; }; - 7309703A36FD4345F1782408988D9A2A /* predictor_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = predictor_enc.c; path = vendor/libwebp/predictor_enc.c; sourceTree = ""; }; - 738AC56CBEF0F5C46F68B5F6C393CE26 /* config_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = config_enc.c; path = vendor/libwebp/config_enc.c; sourceTree = ""; }; - 73BBDBBF3DD175220822CBE95FE0B6DA /* PostHogSurveyResponse.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurveyResponse.swift; path = PostHog/Surveys/Models/PostHogSurveyResponse.swift; sourceTree = ""; }; - 74828AFF764C4FBA1E8273C81B8E961D /* TimeBasedEpochGenerator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TimeBasedEpochGenerator.swift; path = PostHog/Utils/TimeBasedEpochGenerator.swift; sourceTree = ""; }; - 74853741E05F6EF1BEB0A51503A2FAAF /* PostHog-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PostHog-umbrella.h"; sourceTree = ""; }; - 752389685141ECE7C426B3DEB3D9F601 /* PostHogSurvey+Display.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "PostHogSurvey+Display.swift"; path = "PostHog/Models/Surveys/PostHogSurvey+Display.swift"; sourceTree = ""; }; - 770559699B6C4F893D1E7A02F2CD0026 /* PostHogAutocaptureEventTracker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogAutocaptureEventTracker.swift; path = PostHog/Autocapture/PostHogAutocaptureEventTracker.swift; sourceTree = ""; }; - 773CC3FBB2958F1D7CE0C0170B523B93 /* PostHog-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "PostHog-Info.plist"; sourceTree = ""; }; - 77C6B393EA1E57769EC0389F1AD05B9C /* sharpyuv_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = sharpyuv_neon.c; path = vendor/libwebp/sharpyuv_neon.c; sourceTree = ""; }; - 795E63F5459422366F9C7A22AEE66832 /* alpha_processing_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = alpha_processing_sse2.c; path = vendor/libwebp/alpha_processing_sse2.c; sourceTree = ""; }; - 79C664EEE1DC1B7E4C1AD3FFBCE62DCF /* sharpyuv_cpu.c */ = {isa = PBXFileReference; includeInIndex = 1; name = sharpyuv_cpu.c; path = vendor/libwebp/sharpyuv_cpu.c; sourceTree = ""; }; - 7F3CFCC8A376939FB12B819B27393F00 /* ph_quant.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_quant.h; path = vendor/libwebp/ph_quant.h; sourceTree = ""; }; - 801F6B74D8E565882EB6463915EE29D9 /* dec_clip_tables.c */ = {isa = PBXFileReference; includeInIndex = 1; name = dec_clip_tables.c; path = vendor/libwebp/dec_clip_tables.c; sourceTree = ""; }; - 80A1861E13FC4397C27E11FB9264C784 /* ph_backward_references_enc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_backward_references_enc.h; path = vendor/libwebp/ph_backward_references_enc.h; sourceTree = ""; }; - 812FDF6DD4F545B16DF167DAEA77C36B /* DateUtils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateUtils.swift; path = PostHog/Utils/DateUtils.swift; sourceTree = ""; }; - 81A9CA6BA8912B85E00744D072E0AF98 /* ph_types.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_types.h; path = vendor/libwebp/ph_types.h; sourceTree = ""; }; - 8460AF919A0E871BF4A73C742BE5F688 /* quant_levels_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = quant_levels_utils.c; path = vendor/libwebp/quant_levels_utils.c; sourceTree = ""; }; - 84CAF2DEC195826ABDDD705FA32B3BAC /* CGSize+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "CGSize+Util.swift"; path = "PostHog/Replay/CGSize+Util.swift"; sourceTree = ""; }; - 85583DB1C0D12DF398032FE155609A9E /* rescaler_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = rescaler_utils.c; path = vendor/libwebp/rescaler_utils.c; sourceTree = ""; }; - 856534F765DD23D7992D3EC872FE93B7 /* picture_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = picture_enc.c; path = vendor/libwebp/picture_enc.c; sourceTree = ""; }; - 85828150034429DD2DBC8A14669BDF37 /* Pods-CableTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-CableTests-umbrella.h"; sourceTree = ""; }; - 85D33F85427449A3CF37371439A6C570 /* Pods-Cable-CableUITestsScreenshot-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Cable-CableUITestsScreenshot-Info.plist"; sourceTree = ""; }; - 866330A74D6DEBA7354A80F57B4FB378 /* PostHogFileBackedQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogFileBackedQueue.swift; path = PostHog/PostHogFileBackedQueue.swift; sourceTree = ""; }; - 86706A03E7D1636BA28748C6D1F0740A /* filters_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = filters_neon.c; path = vendor/libwebp/filters_neon.c; sourceTree = ""; }; - 873422C94FE37539EBA965E98C7A3636 /* backward_references_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = backward_references_enc.c; path = vendor/libwebp/backward_references_enc.c; sourceTree = ""; }; - 881B0B1AA8FD87B6D79DB655742B357B /* DictUtils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DictUtils.swift; path = PostHog/Utils/DictUtils.swift; sourceTree = ""; }; - 8B72D15908356213E83617F069F28778 /* rescaler_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = rescaler_neon.c; path = vendor/libwebp/rescaler_neon.c; sourceTree = ""; }; - 8C1E609130982209FE02FE99B78BDC88 /* filter_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = filter_enc.c; path = vendor/libwebp/filter_enc.c; sourceTree = ""; }; - 8CECA788D22A8D210E1F126F087FA049 /* dec_sse41.c */ = {isa = PBXFileReference; includeInIndex = 1; name = dec_sse41.c; path = vendor/libwebp/dec_sse41.c; sourceTree = ""; }; - 8DAC933623085F073616F04099AF6703 /* PostHogBatchUploadInfo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogBatchUploadInfo.swift; path = PostHog/PostHogBatchUploadInfo.swift; sourceTree = ""; }; - 8DCCB7DFFA938AEFD75591B645CDDDF0 /* MethodSwizzler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MethodSwizzler.swift; path = PostHog/Replay/MethodSwizzler.swift; sourceTree = ""; }; - 8F310530386C455D52673B41DA4A2B76 /* lossless_sse41.c */ = {isa = PBXFileReference; includeInIndex = 1; name = lossless_sse41.c; path = vendor/libwebp/lossless_sse41.c; sourceTree = ""; }; - 8F5ADA47F04EABAB81527ABBF841D2AC /* Pods-CableTests */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-CableTests"; path = Pods_CableTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 905362F80FAC60800B3E20073D217B0C /* PostHogNoMaskViewModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogNoMaskViewModifier.swift; path = PostHog/SwiftUI/PostHogNoMaskViewModifier.swift; sourceTree = ""; }; - 905D11210787EACE6D48E66560631096 /* URLSessionExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLSessionExtension.swift; path = PostHog/Replay/Plugins/Network/URLSessionExtension.swift; sourceTree = ""; }; - 9145EC49215D8AD4B67DC4C8A3FFCD24 /* View+PostHogLabel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "View+PostHogLabel.swift"; path = "PostHog/Autocapture/SwiftUI/View+PostHogLabel.swift"; sourceTree = ""; }; - 93E56AB9DB0FA2E618B09B988C467DD9 /* ph_cpu.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_cpu.h; path = vendor/libwebp/ph_cpu.h; sourceTree = ""; }; - 94288A5DC18B54C6DC2DD8C571B0F0F6 /* picture_csp_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = picture_csp_enc.c; path = vendor/libwebp/picture_csp_enc.c; sourceTree = ""; }; - 94407C342DD9E167C5D75E705899A981 /* PostHogContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogContext.swift; path = PostHog/PostHogContext.swift; sourceTree = ""; }; - 9567F7671610CAD55DFF871A5F19A42D /* Pods-Cable-CableUITestsScreenshot.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Cable-CableUITestsScreenshot.debug.xcconfig"; sourceTree = ""; }; - 9833327F3E1576D80701526ECD9F245B /* Pods-CableTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-CableTests.debug.xcconfig"; sourceTree = ""; }; - 98C872681D85E83B48A177D688728D6E /* String+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+Util.swift"; path = "PostHog/Replay/String+Util.swift"; sourceTree = ""; }; - 99F4A385A81C30B48330AC1106D13082 /* Pods-Cable */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-Cable"; path = Pods_Cable.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9AC357F49D750FDC096D0A38EF084E0F /* ConfirmationMessage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConfirmationMessage.swift; path = PostHog/Surveys/ConfirmationMessage.swift; sourceTree = ""; }; - 9AE8485816F0A8CEE5E8A00890CA324A /* SurveyPresentationDetentsRepresentable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SurveyPresentationDetentsRepresentable.swift; path = PostHog/Surveys/Utils/SurveyPresentationDetentsRepresentable.swift; sourceTree = ""; }; - 9B2AAEABCF4BAAC96E0099538E6FAEA5 /* PostHog-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "PostHog-dummy.m"; sourceTree = ""; }; - 9C442AD9B5648643BF9F8C662F4B32A7 /* Pods-Cable-CableUITests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Cable-CableUITests-acknowledgements.markdown"; sourceTree = ""; }; - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 9DC16A20D6C39A6FE6C700DE88890FC6 /* dec.c */ = {isa = PBXFileReference; includeInIndex = 1; name = dec.c; path = vendor/libwebp/dec.c; sourceTree = ""; }; - 9E7A2B7242409A2E9C0F0B5B9DBCC3A8 /* lossless_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = lossless_sse2.c; path = vendor/libwebp/lossless_sse2.c; sourceTree = ""; }; - 9F7478CDF58D2E47C91E9AEA4F3BFA36 /* ph_endian_inl_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_endian_inl_utils.h; path = vendor/libwebp/ph_endian_inl_utils.h; sourceTree = ""; }; - 9FBABCFADE0E13362BC515109844B0B7 /* bit_writer_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = bit_writer_utils.c; path = vendor/libwebp/bit_writer_utils.c; sourceTree = ""; }; - 9FF0D333280C1AA92C268AF36D289B8B /* CGColor+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "CGColor+Util.swift"; path = "PostHog/Replay/CGColor+Util.swift"; sourceTree = ""; }; - A2428DC0690BFC906FB6BBC9517C91B8 /* Pods-Cable-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Cable-dummy.m"; sourceTree = ""; }; - A296C0A01AD7CFE6D2C8A9367A6C2A67 /* lossless_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = lossless_neon.c; path = vendor/libwebp/lossless_neon.c; sourceTree = ""; }; - A3B5BAECFF77239E331B522C7107C1A0 /* ApplicationScreenViewPublisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ApplicationScreenViewPublisher.swift; path = "PostHog/Screen Views/ApplicationScreenViewPublisher.swift"; sourceTree = ""; }; - A40B6975FA38F82CFAD982D745DC1177 /* PostHogVersion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogVersion.swift; path = PostHog/PostHogVersion.swift; sourceTree = ""; }; - A4B8C487F55A8DC54688605DFF2114A6 /* ph_neon.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_neon.h; path = vendor/libwebp/ph_neon.h; sourceTree = ""; }; - A50567BD13E8812DF3257A08243491EA /* BottomSection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BottomSection.swift; path = PostHog/Surveys/BottomSection.swift; sourceTree = ""; }; - A64CAB6E8B5412C7633F7B8565B8651D /* ph_random_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_random_utils.h; path = vendor/libwebp/ph_random_utils.h; sourceTree = ""; }; - A7467F12C852FA55CE696623D0C49893 /* cpu.c */ = {isa = PBXFileReference; includeInIndex = 1; name = cpu.c; path = vendor/libwebp/cpu.c; sourceTree = ""; }; - A84BF685A521744850F13C55520AAB70 /* QuestionHeader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = QuestionHeader.swift; path = PostHog/Surveys/QuestionHeader.swift; sourceTree = ""; }; - A9410C5F8607F2FC658BCBEDAD5EEFBE /* histogram_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = histogram_enc.c; path = vendor/libwebp/histogram_enc.c; sourceTree = ""; }; - A97D7894341E91B3BF09503921D83C16 /* ph_palette.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_palette.h; path = vendor/libwebp/ph_palette.h; sourceTree = ""; }; - AC3ADEA740D3607618548A1451E8EE35 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = PostHog/Resources/PrivacyInfo.xcprivacy; sourceTree = ""; }; - AC8C2B6DA50936FEAAA8D7CF5AD97F60 /* ph_vp8_dec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_vp8_dec.h; path = vendor/libwebp/ph_vp8_dec.h; sourceTree = ""; }; - AFC508491302293623F9C9FE123DF31D /* ph_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_utils.h; path = vendor/libwebp/ph_utils.h; sourceTree = ""; }; - B1784FF53C3EB6951476A12994ECAE5A /* Pods-CableTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-CableTests-acknowledgements.markdown"; sourceTree = ""; }; - B1C3DCD4E3C2C0AE65F78D9A92443B29 /* Pods-Cable-CableUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Cable-CableUITests.release.xcconfig"; sourceTree = ""; }; - B233AD5FB3C464116639B4C4A446D5A9 /* yuv_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = yuv_sse2.c; path = vendor/libwebp/yuv_sse2.c; sourceTree = ""; }; - B24A16C60C8C5673028974678594E6E8 /* cost.c */ = {isa = PBXFileReference; includeInIndex = 1; name = cost.c; path = vendor/libwebp/cost.c; sourceTree = ""; }; - B2CD1BA30ECCBAFA941F6D593A91CE5A /* PostHogDisplaySurveyQuestion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogDisplaySurveyQuestion.swift; path = PostHog/Surveys/Models/PostHogDisplaySurveyQuestion.swift; sourceTree = ""; }; - B2E0C12DA5F3B1E2AD47FD377B6B55AD /* Pods-Cable-CableUITestsScreenshot-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Cable-CableUITestsScreenshot-frameworks.sh"; sourceTree = ""; }; - B2FFDD72D120FD8743D882769EF18E9B /* sharpyuv_csp.c */ = {isa = PBXFileReference; includeInIndex = 1; name = sharpyuv_csp.c; path = vendor/libwebp/sharpyuv_csp.c; sourceTree = ""; }; - B3892D9BAABC94C6D1D38A736617D726 /* near_lossless_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = near_lossless_enc.c; path = vendor/libwebp/near_lossless_enc.c; sourceTree = ""; }; - B45CA8B0658CABEFE16DEF11945D4C96 /* PostHogSurveyConditions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurveyConditions.swift; path = PostHog/Models/Surveys/PostHogSurveyConditions.swift; sourceTree = ""; }; - B4AE78BA77D7992329434D3A2D3370F6 /* tree_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = tree_enc.c; path = vendor/libwebp/tree_enc.c; sourceTree = ""; }; - B73ECF10580052B7EADF1C8126C1F5D4 /* enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = enc.c; path = vendor/libwebp/enc.c; sourceTree = ""; }; - BA4A2E54DC65E09C0702AC19D93C51D0 /* PostHogSessionReplayConsoleLogConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSessionReplayConsoleLogConfig.swift; path = PostHog/Replay/PostHogSessionReplayConsoleLogConfig.swift; sourceTree = ""; }; - BADD4B93BF926C2B8CA6E8BAC40F9EED /* EdgeBorder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EdgeBorder.swift; path = PostHog/Surveys/Utils/EdgeBorder.swift; sourceTree = ""; }; - BBAA59A93C40F18F756F62E7761DDDA2 /* PostHogAppLifeCycleIntegration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogAppLifeCycleIntegration.swift; path = "PostHog/App Life Cycle/PostHogAppLifeCycleIntegration.swift"; sourceTree = ""; }; - BC5EDD8394DA064EC7C83ACD7ECCD71E /* Optional+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Optional+Util.swift"; path = "PostHog/Replay/Optional+Util.swift"; sourceTree = ""; }; - BCC15FDAC7750FBA4BA88CC539B5231E /* ph_sharpyuv_cpu.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_sharpyuv_cpu.h; path = vendor/libwebp/ph_sharpyuv_cpu.h; sourceTree = ""; }; - BCE3D06D9F5BD48177BA8DD707F3B037 /* thread_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = thread_utils.c; path = vendor/libwebp/thread_utils.c; sourceTree = ""; }; - BE54094ABB405AD044CBC194CBC3787A /* PostHogConsoleLogInterceptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogConsoleLogInterceptor.swift; path = "PostHog/Replay/Plugins/Console Logs/PostHogConsoleLogInterceptor.swift"; sourceTree = ""; }; - BEA18A14AB5E18A4522909D739010F8B /* PostHogSessionReplayConsoleLogsPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSessionReplayConsoleLogsPlugin.swift; path = "PostHog/Replay/Plugins/Console Logs/PostHogSessionReplayConsoleLogsPlugin.swift"; sourceTree = ""; }; - C07415FC3293C535290E997DFFF959FD /* UIApplication+.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIApplication+.swift"; path = "PostHog/Utils/UIApplication+.swift"; sourceTree = ""; }; - C0B671EE629623603942CB46DA763609 /* AutocaptureEventProcessing.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AutocaptureEventProcessing.swift; path = PostHog/Autocapture/AutocaptureEventProcessing.swift; sourceTree = ""; }; - C15956FAD29CD446AED83B28072F258A /* ph_bit_writer_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_bit_writer_utils.h; path = vendor/libwebp/ph_bit_writer_utils.h; sourceTree = ""; }; - C1C1F47ADEAE9E1F0B9810DF4E04BD56 /* ph_sharpyuv_gamma.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_sharpyuv_gamma.h; path = vendor/libwebp/ph_sharpyuv_gamma.h; sourceTree = ""; }; - C3B1285FF54F13BB960970B7370E7941 /* PostHogAutocaptureIntegration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogAutocaptureIntegration.swift; path = PostHog/Autocapture/PostHogAutocaptureIntegration.swift; sourceTree = ""; }; - C52A7E8E1B3DA2B543DA937C3BE7162B /* upsampling_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = upsampling_neon.c; path = vendor/libwebp/upsampling_neon.c; sourceTree = ""; }; - C60B359AA89658785332F5EB04C61112 /* ApplicationViewLayoutPublisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ApplicationViewLayoutPublisher.swift; path = PostHog/ApplicationViewLayoutPublisher.swift; sourceTree = ""; }; - C720DA9EA026D5E7449597140583E063 /* PostHogLogLevel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogLogLevel.swift; path = "PostHog/Replay/Plugins/Console Logs/PostHogLogLevel.swift"; sourceTree = ""; }; - C77B0E69679F739AA5F6EF3472F81B3E /* Pods-Cable-CableUITests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Cable-CableUITests-dummy.m"; sourceTree = ""; }; - C7C8924423D336E936B9B8D7528391B5 /* PostHogExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogExtensions.swift; path = PostHog/PostHogExtensions.swift; sourceTree = ""; }; - C84990ADFAD35F17E5D87A70DE6B2768 /* PostHogSurvey.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurvey.swift; path = PostHog/Models/Surveys/PostHogSurvey.swift; sourceTree = ""; }; - C87BE5C48779C989CE62FA8282E8B3D5 /* upsampling_sse41.c */ = {isa = PBXFileReference; includeInIndex = 1; name = upsampling_sse41.c; path = vendor/libwebp/upsampling_sse41.c; sourceTree = ""; }; - C94C1436FB9990B1C8458A4890A74FDD /* Pods-Cable-CableUITestsScreenshot-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Cable-CableUITestsScreenshot-umbrella.h"; sourceTree = ""; }; - C95684E80F2639489418BE63DB2999CD /* ph_rescaler_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_rescaler_utils.h; path = vendor/libwebp/ph_rescaler_utils.h; sourceTree = ""; }; - C9A8771BEB422C0EBA1669CCBCC77482 /* SurveysWindow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SurveysWindow.swift; path = PostHog/Surveys/SurveysWindow.swift; sourceTree = ""; }; - CB852437A4FE639A36D1C941F94F307A /* SegmentedControl.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SegmentedControl.swift; path = PostHog/Surveys/Utils/SegmentedControl.swift; sourceTree = ""; }; - CBF4E22F61B73B539ABF63E8ADAAB87E /* yuv.c */ = {isa = PBXFileReference; includeInIndex = 1; name = yuv.c; path = vendor/libwebp/yuv.c; sourceTree = ""; }; - CD9B87A72D9E03E8531BE4E64C96BBAA /* sharpyuv_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = sharpyuv_sse2.c; path = vendor/libwebp/sharpyuv_sse2.c; sourceTree = ""; }; - CDACF4D658D473E1D0C0BC475C6B6D9F /* picture_tools_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = picture_tools_enc.c; path = vendor/libwebp/picture_tools_enc.c; sourceTree = ""; }; - CF7956F6F3924F58644A57ECA19C3559 /* MultipleChoiceOptions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipleChoiceOptions.swift; path = PostHog/Surveys/Utils/MultipleChoiceOptions.swift; sourceTree = ""; }; - CFB8864DE15913957BF2887CC886727E /* PostHogStorageManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogStorageManager.swift; path = PostHog/PostHogStorageManager.swift; sourceTree = ""; }; - D07D529105A58FA284174316608207E1 /* Pods-CableTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-CableTests-dummy.m"; sourceTree = ""; }; - D08666942BF9AA9C3313A5D3639E9A56 /* Pods-Cable-CableUITestsScreenshot-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Cable-CableUITestsScreenshot-acknowledgements.markdown"; sourceTree = ""; }; - D22E0F8ACB0AADC33D8564D1D4E72C93 /* ph_common_dec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_common_dec.h; path = vendor/libwebp/ph_common_dec.h; sourceTree = ""; }; - D3261BD495FD61B20BC8BA98578865B7 /* PostHogNextSurveyQuestion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogNextSurveyQuestion.swift; path = PostHog/Surveys/Models/PostHogNextSurveyQuestion.swift; sourceTree = ""; }; - D396A5F4E5EA6A631CFA1F5BA73BE979 /* ph_decode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_decode.h; path = vendor/libwebp/ph_decode.h; sourceTree = ""; }; - D5DD39201B51B24F484813D108EB3C2C /* Pods-Cable-CableUITestsScreenshot-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Cable-CableUITestsScreenshot-acknowledgements.plist"; sourceTree = ""; }; - D601464067F9E5B1E5D5620C8924912F /* quant_levels_dec_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = quant_levels_dec_utils.c; path = vendor/libwebp/quant_levels_dec_utils.c; sourceTree = ""; }; - D8213698CA3EB61F4236A6C8E334099A /* PostHogEvent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogEvent.swift; path = PostHog/Models/PostHogEvent.swift; sourceTree = ""; }; - D888AE3CE3943C819B306C6E7A8FF6BC /* PostHogSessionManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSessionManager.swift; path = PostHog/PostHogSessionManager.swift; sourceTree = ""; }; - D88D282FD37C24951D615E881B2D8B5E /* PostHogMaskViewModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogMaskViewModifier.swift; path = PostHog/SwiftUI/PostHogMaskViewModifier.swift; sourceTree = ""; }; - D8AAD180580FCC6EE9AEFFBE6AEDA671 /* Pods-Cable-CableUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Cable-CableUITests.debug.xcconfig"; sourceTree = ""; }; - D8ACAC7F2DBEC2E6F6C08E461831738E /* PostHogPropertiesSanitizer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogPropertiesSanitizer.swift; path = PostHog/PostHogPropertiesSanitizer.swift; sourceTree = ""; }; - DA9D1BD5EFA6A8CA76078ADFBB131D2D /* PostHog.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PostHog.debug.xcconfig; sourceTree = ""; }; - DB9CEA743E2F233A85546C94DD807354 /* sharpyuv_dsp.c */ = {isa = PBXFileReference; includeInIndex = 1; name = sharpyuv_dsp.c; path = vendor/libwebp/sharpyuv_dsp.c; sourceTree = ""; }; - DCDFFA010640D140217DEAF48821ABD1 /* PostHog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PostHog.release.xcconfig; sourceTree = ""; }; - DD8F109A9D1262A0B31CFFE54558EF8E /* Pods-Cable.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Cable.debug.xcconfig"; sourceTree = ""; }; - DDC359F157B7B3B8BA8CEDD5389D7EC4 /* analysis_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = analysis_enc.c; path = vendor/libwebp/analysis_enc.c; sourceTree = ""; }; - DE79FAF47ADBBDA1FD96FA4EB80CD2D4 /* ph_huffman_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_huffman_utils.h; path = vendor/libwebp/ph_huffman_utils.h; sourceTree = ""; }; - DE9FD3D6919DF785A204354C43DE839D /* Pods-Cable.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Cable.release.xcconfig"; sourceTree = ""; }; - DF049858415B4235BCDBB079C3581BA4 /* color_cache_utils.c */ = {isa = PBXFileReference; includeInIndex = 1; name = color_cache_utils.c; path = vendor/libwebp/color_cache_utils.c; sourceTree = ""; }; - DF2A3A811E0015722FF7E34E9338B448 /* sharpyuv.c */ = {isa = PBXFileReference; includeInIndex = 1; name = sharpyuv.c; path = vendor/libwebp/sharpyuv.c; sourceTree = ""; }; - E0D796ED35657AF32721999BD872B40E /* lossless_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = lossless_enc.c; path = vendor/libwebp/lossless_enc.c; sourceTree = ""; }; - E20DB8D1A1DFFDC41590109620887074 /* ph_muxi.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_muxi.h; path = vendor/libwebp/ph_muxi.h; sourceTree = ""; }; - E3D701A5BB86EA2FBA69F57589340667 /* UIView+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIView+Util.swift"; path = "PostHog/Replay/UIView+Util.swift"; sourceTree = ""; }; - E634ED9E4F291E94869F8F535E2FB59D /* enc_neon.c */ = {isa = PBXFileReference; includeInIndex = 1; name = enc_neon.c; path = vendor/libwebp/enc_neon.c; sourceTree = ""; }; - E71BC82ADF0D9EC4F9A390C9D17DAEB7 /* ph_sharpyuv.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_sharpyuv.h; path = vendor/libwebp/ph_sharpyuv.h; sourceTree = ""; }; - E72753E6126AC46A7475C95A3F9DE5A7 /* Pods-CableTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-CableTests.modulemap"; sourceTree = ""; }; - E7F5A16999097A6591248430D5EE1DD5 /* enc_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = enc_sse2.c; path = vendor/libwebp/enc_sse2.c; sourceTree = ""; }; - E851D31270CCB1CA24F49C52015B7F59 /* ph_mux_types.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_mux_types.h; path = vendor/libwebp/ph_mux_types.h; sourceTree = ""; }; - EAA1A0EEEA8915D7ED932898580D55DA /* ViewTreeSnapshotStatus.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ViewTreeSnapshotStatus.swift; path = PostHog/Replay/ViewTreeSnapshotStatus.swift; sourceTree = ""; }; - EC7A62B6B9F7CF772B7553E3B238AAB3 /* UIColor+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIColor+Util.swift"; path = "PostHog/Replay/UIColor+Util.swift"; sourceTree = ""; }; - ECD1B7D205F7FC63D45533FC2BAE3410 /* sharpyuv_gamma.c */ = {isa = PBXFileReference; includeInIndex = 1; name = sharpyuv_gamma.c; path = vendor/libwebp/sharpyuv_gamma.c; sourceTree = ""; }; - ED013EDE4144AF0F72EA8632E927919E /* frame_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = frame_enc.c; path = vendor/libwebp/frame_enc.c; sourceTree = ""; }; - ED3CF8C4E1C8C5343DE62E4B8A2043AD /* PostHogQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogQueue.swift; path = PostHog/PostHogQueue.swift; sourceTree = ""; }; - ED8BAA4BA5693210FDA9BE42AA76F93E /* PostHogPersonProfiles.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogPersonProfiles.swift; path = PostHog/PostHogPersonProfiles.swift; sourceTree = ""; }; - EDA36D795E2D2304CF2F39068E99EED8 /* RRWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RRWireframe.swift; path = PostHog/Replay/RRWireframe.swift; sourceTree = ""; }; - EDFECD65E342D7E6925201E956D8FBDA /* PostHog-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PostHog-prefix.pch"; sourceTree = ""; }; - EEA4A4F055CC07589E72A35CB0E1EF28 /* ph_sharpyuv_dsp.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_sharpyuv_dsp.h; path = vendor/libwebp/ph_sharpyuv_dsp.h; sourceTree = ""; }; - EF48F37324DDA42ABC08BC0E63AE23FC /* PostHogSDK.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSDK.swift; path = PostHog/PostHogSDK.swift; sourceTree = ""; }; - EF645AFD51E32C0A7B929FE3710B3EC9 /* Pods-Cable-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Cable-acknowledgements.markdown"; sourceTree = ""; }; - EFBDDE4003994362E3FE58381610B087 /* lossless_enc_sse41.c */ = {isa = PBXFileReference; includeInIndex = 1; name = lossless_enc_sse41.c; path = vendor/libwebp/lossless_enc_sse41.c; sourceTree = ""; }; - F091F375187698A52912BC097838826A /* Float+Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Float+Util.swift"; path = "PostHog/Replay/Float+Util.swift"; sourceTree = ""; }; - F115DBC1009A4AE8B1E2EEACECEE686D /* ph_huffman_encode_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_huffman_encode_utils.h; path = vendor/libwebp/ph_huffman_encode_utils.h; sourceTree = ""; }; - F3BBC9AA886853862CBA71BF1966D0F2 /* picture_rescale_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = picture_rescale_enc.c; path = vendor/libwebp/picture_rescale_enc.c; sourceTree = ""; }; - F45BEC0B42FB9EFA2C1B6AADA6AAF9E4 /* alpha_processing.c */ = {isa = PBXFileReference; includeInIndex = 1; name = alpha_processing.c; path = vendor/libwebp/alpha_processing.c; sourceTree = ""; }; - F48FB46A5EECE30CA2951FE024EEE7C6 /* Pods-Cable-CableUITestsScreenshot */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-Cable-CableUITestsScreenshot"; path = Pods_Cable_CableUITestsScreenshot.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F4C64C6E9AA1FDB5C402603F7B39F3BC /* ph_common_sse2.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_common_sse2.h; path = vendor/libwebp/ph_common_sse2.h; sourceTree = ""; }; - F553AE255E9F4CBC202ED457EF76082F /* lossless.c */ = {isa = PBXFileReference; includeInIndex = 1; name = lossless.c; path = vendor/libwebp/lossless.c; sourceTree = ""; }; - F6DC89C77E43B360E6ED162E84069CF6 /* Pods-Cable-CableUITestsScreenshot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Cable-CableUITestsScreenshot.release.xcconfig"; sourceTree = ""; }; - F85D4CA294CDFB70EE341652BCF427BC /* cost_sse2.c */ = {isa = PBXFileReference; includeInIndex = 1; name = cost_sse2.c; path = vendor/libwebp/cost_sse2.c; sourceTree = ""; }; - F9D50B5F78458141D8272AA365385479 /* PostHogSwiftUIViewModifiers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSwiftUIViewModifiers.swift; path = PostHog/SwiftUI/PostHogSwiftUIViewModifiers.swift; sourceTree = ""; }; - FA884DB8D0FD6DE10EC0E09ECBCB0FCC /* PostHogSurveyQuestion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostHogSurveyQuestion.swift; path = PostHog/Models/Surveys/PostHogSurveyQuestion.swift; sourceTree = ""; }; - FB3C8E706E1BB31142E0F7F3BDAAD7A3 /* ph_format_constants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_format_constants.h; path = vendor/libwebp/ph_format_constants.h; sourceTree = ""; }; - FBA8E6C91C9871D781A894E9021E767F /* ph_cost_enc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_cost_enc.h; path = vendor/libwebp/ph_cost_enc.h; sourceTree = ""; }; - FC51F4877491548B2CE8612D8EBE8994 /* ph_mux.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ph_mux.h; path = vendor/libwebp/ph_mux.h; sourceTree = ""; }; - FD4EE07A95F242B51F29985FC791B86C /* picture_psnr_enc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = picture_psnr_enc.c; path = vendor/libwebp/picture_psnr_enc.c; sourceTree = ""; }; - FE3276BC9DD9343402F74F0411ACBAD3 /* Pods-Cable-CableUITests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Cable-CableUITests-Info.plist"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 3890B9438DD6516F1EA5E3CAACCE8FFE /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 6CCFABEA9067B84D78A97723B7424B2A /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 464D3A2144371ABCE15B36C2A6A868B9 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - EAEEFEB5F2F8EDC54B52B4FEE8E0021E /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6914A7F0DD12E15F1B99EAE8F381191A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 6A604351BC7DAE8F47EADA54FA67BA92 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 73D22CAADE4FCD69C0A715C06D35F9B8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A26CDBEA01200B96B68F314398EEE38C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4006CE8FE027629C962E1BFB2A56F858 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EA42357D9D8899D007F3BE4D5BAB8571 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - E0749957A6BB7594DC26469DB77B0C7B /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0223C71E4F7A4926A35DE003CAC38985 /* PostHog */ = { - isa = PBXGroup; - children = ( - 02F6988AFBC0B2AF511F86755F935B13 /* alpha_enc.c */, - F45BEC0B42FB9EFA2C1B6AADA6AAF9E4 /* alpha_processing.c */, - 6293FFF4CAA83AC8938A5AF805A16B4E /* alpha_processing_neon.c */, - 795E63F5459422366F9C7A22AEE66832 /* alpha_processing_sse2.c */, - 676CFD3B35C9F8564C51E1B617A3937C /* alpha_processing_sse41.c */, - DDC359F157B7B3B8BA8CEDD5389D7EC4 /* analysis_enc.c */, - 400531B7575C215F76B0965A3F16DDA0 /* ApplicationEventPublisher.swift */, - 1F788308CE59B5B07BDA5820491EC32B /* ApplicationLifecyclePublisher.swift */, - A3B5BAECFF77239E331B522C7107C1A0 /* ApplicationScreenViewPublisher.swift */, - C60B359AA89658785332F5EB04C61112 /* ApplicationViewLayoutPublisher.swift */, - 15861BEA1F90094B56DC2CD840469B3D /* AssociatedKeys.swift */, - C0B671EE629623603942CB46DA763609 /* AutocaptureEventProcessing.swift */, - 716805E0DE609F449A51C263A653D182 /* backward_references_cost_enc.c */, - 873422C94FE37539EBA965E98C7A3636 /* backward_references_enc.c */, - 143960F19300CE4777F0CC55F8DD8E31 /* bit_reader_utils.c */, - 9FBABCFADE0E13362BC515109844B0B7 /* bit_writer_utils.c */, - A50567BD13E8812DF3257A08243491EA /* BottomSection.swift */, - 9FF0D333280C1AA92C268AF36D289B8B /* CGColor+Util.swift */, - 84CAF2DEC195826ABDDD705FA32B3BAC /* CGSize+Util.swift */, - DF049858415B4235BCDBB079C3581BA4 /* color_cache_utils.c */, - 738AC56CBEF0F5C46F68B5F6C393CE26 /* config_enc.c */, - 9AC357F49D750FDC096D0A38EF084E0F /* ConfirmationMessage.swift */, - B24A16C60C8C5673028974678594E6E8 /* cost.c */, - 02AE2A2A0467EA70DB5D9C6CB93B7CBE /* cost_enc.c */, - 3BBA09ECA091215738C1BE2FFE5F091C /* cost_neon.c */, - F85D4CA294CDFB70EE341652BCF427BC /* cost_sse2.c */, - A7467F12C852FA55CE696623D0C49893 /* cpu.c */, - 0CFF0D65E2D3133D28C9527F218B033B /* Data+Gzip.swift */, - 07439B205D54D4542249407BDC340D8B /* Date+Util.swift */, - 812FDF6DD4F545B16DF167DAEA77C36B /* DateUtils.swift */, - 9DC16A20D6C39A6FE6C700DE88890FC6 /* dec.c */, - 801F6B74D8E565882EB6463915EE29D9 /* dec_clip_tables.c */, - 0093492C1008E66439CF39ACE752BD1B /* dec_neon.c */, - 1B3EF635F2042C38745AADD3B049F9D1 /* dec_sse2.c */, - 8CECA788D22A8D210E1F126F087FA049 /* dec_sse41.c */, - 6DB3C2029FEE43C9492E48815A749CD8 /* DI.swift */, - 881B0B1AA8FD87B6D79DB655742B357B /* DictUtils.swift */, - BADD4B93BF926C2B8CA6E8BAC40F9EED /* EdgeBorder.swift */, - 07ACC03496AD0255B5AF70CE5B4D3739 /* EmojiRating.swift */, - B73ECF10580052B7EADF1C8126C1F5D4 /* enc.c */, - E634ED9E4F291E94869F8F535E2FB59D /* enc_neon.c */, - E7F5A16999097A6591248430D5EE1DD5 /* enc_sse2.c */, - 399AFF4367999AB195B1A4861DE2E3E1 /* enc_sse41.c */, - 34EBB44BB3ED427636A0A0A673F61914 /* Errors.swift */, - 13289E1D4EAA79792BF87E290976C758 /* FileUtils.swift */, - 8C1E609130982209FE02FE99B78BDC88 /* filter_enc.c */, - 22394C97F8C3BED5DFED7C780C1CE564 /* filters.c */, - 86706A03E7D1636BA28748C6D1F0740A /* filters_neon.c */, - 13CD7EE11BBD992CE37375834390CA29 /* filters_sse2.c */, - 262DE51E90B8F78B18D4D167AAC6C4B4 /* filters_utils.c */, - F091F375187698A52912BC097838826A /* Float+Util.swift */, - 1BC3CBC81194A98EBD7798839CE8D8C4 /* ForwardingPickerViewDelegate.swift */, - ED013EDE4144AF0F72EA8632E927919E /* frame_enc.c */, - 6C3B8263979EC31683158AC2B8D195C7 /* Hedgelog.swift */, - A9410C5F8607F2FC658BCBEDAD5EEFBE /* histogram_enc.c */, - 2D78E84DEDA315BA0155AF3658637CFC /* huffman_encode_utils.c */, - 16D25A3C7A45F1954C4E6B068C58B7EA /* huffman_utils.c */, - 4282C330B006663023D6DA60644E9C71 /* iterator_enc.c */, - F553AE255E9F4CBC202ED457EF76082F /* lossless.c */, - E0D796ED35657AF32721999BD872B40E /* lossless_enc.c */, - 69AF4F959032087EDA8FC0D0A5B6209D /* lossless_enc_neon.c */, - 53A2AF1A2EBA24407AC0E0E425DED2AB /* lossless_enc_sse2.c */, - EFBDDE4003994362E3FE58381610B087 /* lossless_enc_sse41.c */, - A296C0A01AD7CFE6D2C8A9367A6C2A67 /* lossless_neon.c */, - 9E7A2B7242409A2E9C0F0B5B9DBCC3A8 /* lossless_sse2.c */, - 8F310530386C455D52673B41DA4A2B76 /* lossless_sse41.c */, - 8DCCB7DFFA938AEFD75591B645CDDDF0 /* MethodSwizzler.swift */, - CF7956F6F3924F58644A57ECA19C3559 /* MultipleChoiceOptions.swift */, - 172A611FED667B809856910CFB1F8DDA /* muxedit.c */, - 58890CE1C13F1D218F91B8F1B662297C /* muxinternal.c */, - 42AC22285670786E35A95F104334D436 /* muxread.c */, - B3892D9BAABC94C6D1D38A736617D726 /* near_lossless_enc.c */, - 516914075585022AAE4D7BBA974FC678 /* NetworkSample.swift */, - 0B5738CAC80AE5D72B75358EEE9D7559 /* NumberRating.swift */, - BC5EDD8394DA064EC7C83ACD7ECCD71E /* Optional+Util.swift */, - 4B69F8B2559E5EE459C85C21EFEE836B /* palette.c */, - 80A1861E13FC4397C27E11FB9264C784 /* ph_backward_references_enc.h */, - 114279FBFB8CEFB0597BCACAFD61F21C /* ph_bit_reader_utils.h */, - C15956FAD29CD446AED83B28072F258A /* ph_bit_writer_utils.h */, - 10FE6C33488F83B12D9A37C71584E723 /* ph_color_cache_utils.h */, - D22E0F8ACB0AADC33D8564D1D4E72C93 /* ph_common_dec.h */, - F4C64C6E9AA1FDB5C402603F7B39F3BC /* ph_common_sse2.h */, - 3B8A9E04574F84317E10956DD24EE006 /* ph_common_sse41.h */, - FBA8E6C91C9871D781A894E9021E767F /* ph_cost_enc.h */, - 93E56AB9DB0FA2E618B09B988C467DD9 /* ph_cpu.h */, - D396A5F4E5EA6A631CFA1F5BA73BE979 /* ph_decode.h */, - 4BFDFC30C1DB0FBE9E1AAB4CA5D5E990 /* ph_dsp.h */, - 5A40B8F5231A7944996ACCC1E340899D /* ph_encode.h */, - 9F7478CDF58D2E47C91E9AEA4F3BFA36 /* ph_endian_inl_utils.h */, - 450735A51B25E2BC879F80FE3EF683E2 /* ph_filters_utils.h */, - FB3C8E706E1BB31142E0F7F3BDAAD7A3 /* ph_format_constants.h */, - 1DA4E5C73A12E0A6898B70F3A9CED1A6 /* ph_histogram_enc.h */, - F115DBC1009A4AE8B1E2EEACECEE686D /* ph_huffman_encode_utils.h */, - DE79FAF47ADBBDA1FD96FA4EB80CD2D4 /* ph_huffman_utils.h */, - 6ABF4049707FD7850C0863E5B99FC269 /* ph_lossless.h */, - 44B2D98D590BDDF72420B7AC9EE530EF /* ph_lossless_common.h */, - FC51F4877491548B2CE8612D8EBE8994 /* ph_mux.h */, - E851D31270CCB1CA24F49C52015B7F59 /* ph_mux_types.h */, - E20DB8D1A1DFFDC41590109620887074 /* ph_muxi.h */, - A4B8C487F55A8DC54688605DFF2114A6 /* ph_neon.h */, - A97D7894341E91B3BF09503921D83C16 /* ph_palette.h */, - 7F3CFCC8A376939FB12B819B27393F00 /* ph_quant.h */, - 0C5591E49085B223152BC20087DBC2F5 /* ph_quant_levels_utils.h */, - A64CAB6E8B5412C7633F7B8565B8651D /* ph_random_utils.h */, - C95684E80F2639489418BE63DB2999CD /* ph_rescaler_utils.h */, - E71BC82ADF0D9EC4F9A390C9D17DAEB7 /* ph_sharpyuv.h */, - BCC15FDAC7750FBA4BA88CC539B5231E /* ph_sharpyuv_cpu.h */, - 1E3A1E4CB22BAA257379DDFFB6654CC3 /* ph_sharpyuv_csp.h */, - EEA4A4F055CC07589E72A35CB0E1EF28 /* ph_sharpyuv_dsp.h */, - C1C1F47ADEAE9E1F0B9810DF4E04BD56 /* ph_sharpyuv_gamma.h */, - 52ABD4159B83070BF140003F701169DE /* ph_thread_utils.h */, - 81A9CA6BA8912B85E00744D072E0AF98 /* ph_types.h */, - AFC508491302293623F9C9FE123DF31D /* ph_utils.h */, - AC8C2B6DA50936FEAAA8D7CF5AD97F60 /* ph_vp8_dec.h */, - 63BDD7A067C75995C9F44A5BD44EE5E7 /* ph_vp8i_dec.h */, - 27378C145AF70BB39C853D63F329CD6D /* ph_vp8i_enc.h */, - 229D517849A145DB966B04AE150168FB /* ph_vp8li_dec.h */, - 268E4C94970868F0267B55DD268C4FDC /* ph_vp8li_enc.h */, - 44A2B199B29D72CB41D7DCC66EE77960 /* ph_webpi_dec.h */, - 6F05DC32B52DAD2AA8964AB408980A1F /* ph_yuv.h */, - 94288A5DC18B54C6DC2DD8C571B0F0F6 /* picture_csp_enc.c */, - 856534F765DD23D7992D3EC872FE93B7 /* picture_enc.c */, - FD4EE07A95F242B51F29985FC791B86C /* picture_psnr_enc.c */, - F3BBC9AA886853862CBA71BF1966D0F2 /* picture_rescale_enc.c */, - CDACF4D658D473E1D0C0BC475C6B6D9F /* picture_tools_enc.c */, - 67DFC8AB8EF46C4E3F28229F5DCE1C8C /* PostHog.h */, - 5A8552B95757A4F529060DCD92FA9EA8 /* PostHogApi.swift */, - BBAA59A93C40F18F756F62E7761DDDA2 /* PostHogAppLifeCycleIntegration.swift */, - 770559699B6C4F893D1E7A02F2CD0026 /* PostHogAutocaptureEventTracker.swift */, - C3B1285FF54F13BB960970B7370E7941 /* PostHogAutocaptureIntegration.swift */, - 8DAC933623085F073616F04099AF6703 /* PostHogBatchUploadInfo.swift */, - 5C042A1CBFE280735CE602F869E5E16E /* PostHogConfig.swift */, - BE54094ABB405AD044CBC194CBC3787A /* PostHogConsoleLogInterceptor.swift */, - 1FDB8663680B7CBA9F5E40A296878638 /* PostHogConsumerPayload.swift */, - 94407C342DD9E167C5D75E705899A981 /* PostHogContext.swift */, - 66AFFADA1464DFC165AF775D6EDDE652 /* PostHogDisplaySurvey.swift */, - 56F211460A3349D6478176319237385E /* PostHogDisplaySurveyAppearance.swift */, - B2CD1BA30ECCBAFA941F6D593A91CE5A /* PostHogDisplaySurveyQuestion.swift */, - D8213698CA3EB61F4236A6C8E334099A /* PostHogEvent.swift */, - C7C8924423D336E936B9B8D7528391B5 /* PostHogExtensions.swift */, - 866330A74D6DEBA7354A80F57B4FB378 /* PostHogFileBackedQueue.swift */, - 58006037934279EEB6F765806CE31D38 /* PostHogIntegration.swift */, - 0E6E0620A6F6472F44291ECF29967CCB /* PostHogLegacyQueue.swift */, - 03F6F40CA78AF34A91BC6DEC8595DF55 /* PostHogLogEntry.swift */, - C720DA9EA026D5E7449597140583E063 /* PostHogLogLevel.swift */, - D88D282FD37C24951D615E881B2D8B5E /* PostHogMaskViewModifier.swift */, - D3261BD495FD61B20BC8BA98578865B7 /* PostHogNextSurveyQuestion.swift */, - 905362F80FAC60800B3E20073D217B0C /* PostHogNoMaskViewModifier.swift */, - ED8BAA4BA5693210FDA9BE42AA76F93E /* PostHogPersonProfiles.swift */, - D8ACAC7F2DBEC2E6F6C08E461831738E /* PostHogPropertiesSanitizer.swift */, - ED3CF8C4E1C8C5343DE62E4B8A2043AD /* PostHogQueue.swift */, - 05688B486C05767CC332F7BF18F63FA6 /* PostHogRemoteConfig.swift */, - 36EBA9BB4949682419A03F0FE285AFDD /* PostHogReplayIntegration.swift */, - 49AA24A61F055E7B69C6A5D8C6B2AD78 /* PostHogScreenViewIntegration.swift */, - EF48F37324DDA42ABC08BC0E63AE23FC /* PostHogSDK.swift */, - D888AE3CE3943C819B306C6E7A8FF6BC /* PostHogSessionManager.swift */, - 103153A1783959BDADE5A086BAEE932E /* PostHogSessionReplayConfig.swift */, - BA4A2E54DC65E09C0702AC19D93C51D0 /* PostHogSessionReplayConsoleLogConfig.swift */, - BEA18A14AB5E18A4522909D739010F8B /* PostHogSessionReplayConsoleLogsPlugin.swift */, - 52C45951D83FB2A2320DCC090D6EE882 /* PostHogSessionReplayNetworkPlugin.swift */, - 4B180FB52B54B3D7CE6A1EB92AF8CD1F /* PostHogSessionReplayPlugin.swift */, - 1B7E943BABB7F3EAC5DE3E570F21D845 /* PostHogStorage.swift */, - CFB8864DE15913957BF2887CC886727E /* PostHogStorageManager.swift */, - C84990ADFAD35F17E5D87A70DE6B2768 /* PostHogSurvey.swift */, - 752389685141ECE7C426B3DEB3D9F601 /* PostHogSurvey+Display.swift */, - 2AB66D01B3E08701E5AD477ECC306070 /* PostHogSurveyAppearance.swift */, - B45CA8B0658CABEFE16DEF11945D4C96 /* PostHogSurveyConditions.swift */, - 701958AB5C27BF0CCC2736BED2525ADA /* PostHogSurveyEnums.swift */, - 1B69967DBE2F79C655769D9E766052B9 /* PostHogSurveyIntegration.swift */, - FA884DB8D0FD6DE10EC0E09ECBCB0FCC /* PostHogSurveyQuestion.swift */, - 73BBDBBF3DD175220822CBE95FE0B6DA /* PostHogSurveyResponse.swift */, - 15D68B2FA6D4A041D8698FC67954D8CF /* PostHogSurveysConfig.swift */, - 57AED642173AA14A5AB8433E3D4B85ED /* PostHogSurveysDefaultDelegate.swift */, - F9D50B5F78458141D8272AA365385479 /* PostHogSwiftUIViewModifiers.swift */, - 69AC865B00E170495D08F65246F26683 /* PostHogSwizzler.swift */, - 267A77D966A9CB080522F80053CB2597 /* PostHogTagViewModifier.swift */, - A40B6975FA38F82CFAD982D745DC1177 /* PostHogVersion.swift */, - 7309703A36FD4345F1782408988D9A2A /* predictor_enc.c */, - 58B2390554BF82073BFFF56330375E16 /* quant_enc.c */, - D601464067F9E5B1E5D5620C8924912F /* quant_levels_dec_utils.c */, - 8460AF919A0E871BF4A73C742BE5F688 /* quant_levels_utils.c */, - A84BF685A521744850F13C55520AAB70 /* QuestionHeader.swift */, - 3C816478A7A89278F0B9617CD33B4ABB /* QuestionTypes.swift */, - 276E633F3287EC40CFDCCA704F85A7BB /* random_utils.c */, - 6E80C814BD248103DB298C992AB07604 /* Reachability.swift */, - 1F97F6C297E221C6D17A28FD64FD56C9 /* ReadWriteLock.swift */, - 2DE0CB3567095D87C771243576EF9353 /* rescaler.c */, - 8B72D15908356213E83617F069F28778 /* rescaler_neon.c */, - 3EA95D0670D5D47F7BC69002970C0E15 /* rescaler_sse2.c */, - 85583DB1C0D12DF398032FE155609A9E /* rescaler_utils.c */, - 30358A66B1E325E7BF8FACB77136DCBD /* Resources.swift */, - 6EC9E8EDC00E76181958B8A45361E2E3 /* RRStyle.swift */, - EDA36D795E2D2304CF2F39068E99EED8 /* RRWireframe.swift */, - CB852437A4FE639A36D1C941F94F307A /* SegmentedControl.swift */, - DF2A3A811E0015722FF7E34E9338B448 /* sharpyuv.c */, - 79C664EEE1DC1B7E4C1AD3FFBCE62DCF /* sharpyuv_cpu.c */, - B2FFDD72D120FD8743D882769EF18E9B /* sharpyuv_csp.c */, - DB9CEA743E2F233A85546C94DD807354 /* sharpyuv_dsp.c */, - ECD1B7D205F7FC63D45533FC2BAE3410 /* sharpyuv_gamma.c */, - 77C6B393EA1E57769EC0389F1AD05B9C /* sharpyuv_neon.c */, - CD9B87A72D9E03E8531BE4E64C96BBAA /* sharpyuv_sse2.c */, - 06902488BC3ACDB66C4CC8E3DA9B2BF1 /* ssim.c */, - 71C01AE5C1556B7FCEB70598A2359C57 /* ssim_sse2.c */, - 98C872681D85E83B48A177D688728D6E /* String+Util.swift */, - 26FE4D61E0E5E088E0A627B0FDFC294E /* Survey+Util.swift */, - 262A25E1DBCEB2502F06974ED0FE5FBD /* SurveyButton.swift */, - 635FCA4635E4DDDA1A3FC3953FC4C9E9 /* SurveyDisplayController.swift */, - 9AE8485816F0A8CEE5E8A00890CA324A /* SurveyPresentationDetentsRepresentable.swift */, - 6304E8E5FDED5863B9DCE550E79F2F13 /* SurveySheet.swift */, - 4AF87321A55BD1044FFD6A604445B369 /* SurveysRootView.swift */, - C9A8771BEB422C0EBA1669CCBCC77482 /* SurveysWindow.swift */, - 28E5B198F689C1C063C1DE0BF9B7ABAF /* SwiftUI+Util.swift */, - 390E5F5970C7EFFE8A499DFEFE839B86 /* syntax_enc.c */, - BCE3D06D9F5BD48177BA8DD707F3B037 /* thread_utils.c */, - 74828AFF764C4FBA1E8273C81B8E961D /* TimeBasedEpochGenerator.swift */, - 04F316A4B2CB95903F1476FD8F6E5506 /* token_enc.c */, - B4AE78BA77D7992329434D3A2D3370F6 /* tree_enc.c */, - C07415FC3293C535290E997DFFF959FD /* UIApplication+.swift */, - EC7A62B6B9F7CF772B7553E3B238AAB3 /* UIColor+Util.swift */, - 497BF2253E737EA49DACF8B6460E188A /* UIImage+Util.swift */, - 5131F1FFA6BB84612F7867F86C51FCBE /* UIImage+WebP.swift */, - 1DB59137CCCF0AB605EA3C16F30B47C7 /* UITextInputTraits+Util.swift */, - 394497CE62FB8F5E127D55A66D718803 /* UIView+PostHogLabel.swift */, - E3D701A5BB86EA2FBA69F57589340667 /* UIView+Util.swift */, - 1A884B1BB87102903507CF0084AE903C /* UIViewController.swift */, - 54D1B502E8A8D8A7CF0F160485F27D94 /* UIWindow+.swift */, - 5C1E2A40BEFB3E27559CF75D3AF598D0 /* upsampling.c */, - C52A7E8E1B3DA2B543DA937C3BE7162B /* upsampling_neon.c */, - 58E3377D17B30CB16D0310778D68FF1B /* upsampling_sse2.c */, - C87BE5C48779C989CE62FA8282E8B3D5 /* upsampling_sse41.c */, - 905D11210787EACE6D48E66560631096 /* URLSessionExtension.swift */, - 439F6F8B57A9D0DC1E4C4669A0B6BD70 /* URLSessionInterceptor.swift */, - 1CACEF186F8DF80FAFD17F7D33BC5D71 /* URLSessionSwizzler.swift */, - 24177CAF8EE40225DC590E24E98B5907 /* utils.c */, - 39451BDDC73A8B6E24FFE97D8F418255 /* UUIDUtils.swift */, - 9145EC49215D8AD4B67DC4C8A3FFCD24 /* View+PostHogLabel.swift */, - EAA1A0EEEA8915D7ED932898580D55DA /* ViewTreeSnapshotStatus.swift */, - 1900B13AA3B133ACDED32587C775ABA2 /* vp8l_enc.c */, - 46FD7041B91F02D8C44A8B23724610E8 /* webp_enc.c */, - CBF4E22F61B73B539ABF63E8ADAAB87E /* yuv.c */, - 235F09390CCC154EF3CE1AF84501B9D5 /* yuv_neon.c */, - B233AD5FB3C464116639B4C4A446D5A9 /* yuv_sse2.c */, - 30238EC8C0FAB5B9682951C8C40A56BC /* yuv_sse41.c */, - 2D5E9A1C5E8CEAE871AA6E0BECC9314A /* Resources */, - 2F453DBFE7C2D6DF985DB9E1B233D9AC /* Support Files */, - ); - name = PostHog; - path = PostHog; - sourceTree = ""; - }; - 1484FEE6BEED2D255564FAF6215F4252 /* Pods-CableTests */ = { - isa = PBXGroup; - children = ( - E72753E6126AC46A7475C95A3F9DE5A7 /* Pods-CableTests.modulemap */, - B1784FF53C3EB6951476A12994ECAE5A /* Pods-CableTests-acknowledgements.markdown */, - 5633C72981EE1D3DFED81A8579AA5217 /* Pods-CableTests-acknowledgements.plist */, - D07D529105A58FA284174316608207E1 /* Pods-CableTests-dummy.m */, - 3AE65FACEDE1F85D4627DDF23A372ED1 /* Pods-CableTests-Info.plist */, - 85828150034429DD2DBC8A14669BDF37 /* Pods-CableTests-umbrella.h */, - 9833327F3E1576D80701526ECD9F245B /* Pods-CableTests.debug.xcconfig */, - 54A7FFEE9A3DE4A9543516D400145E56 /* Pods-CableTests.release.xcconfig */, - ); - name = "Pods-CableTests"; - path = "Target Support Files/Pods-CableTests"; - sourceTree = ""; - }; - 1CA9B4AF47F09AED5B697E849F55B43E /* Targets Support Files */ = { - isa = PBXGroup; - children = ( - ED72A752F3C64B6966D7D367BFAE6679 /* Pods-Cable */, - EB416C5BECB04A8A0EE73DFB48A2DD95 /* Pods-Cable-CableUITests */, - 383A41246B730B9633A2A1E341D6324B /* Pods-Cable-CableUITestsScreenshot */, - 1484FEE6BEED2D255564FAF6215F4252 /* Pods-CableTests */, - ); - name = "Targets Support Files"; - sourceTree = ""; - }; - 2D5E9A1C5E8CEAE871AA6E0BECC9314A /* Resources */ = { - isa = PBXGroup; - children = ( - AC3ADEA740D3607618548A1451E8EE35 /* PrivacyInfo.xcprivacy */, - ); - name = Resources; - sourceTree = ""; - }; - 2F453DBFE7C2D6DF985DB9E1B233D9AC /* Support Files */ = { - isa = PBXGroup; - children = ( - 1615B66B1E3F08A9CCE5056265CA80A8 /* PostHog.modulemap */, - 9B2AAEABCF4BAAC96E0099538E6FAEA5 /* PostHog-dummy.m */, - 773CC3FBB2958F1D7CE0C0170B523B93 /* PostHog-Info.plist */, - EDFECD65E342D7E6925201E956D8FBDA /* PostHog-prefix.pch */, - 74853741E05F6EF1BEB0A51503A2FAAF /* PostHog-umbrella.h */, - DA9D1BD5EFA6A8CA76078ADFBB131D2D /* PostHog.debug.xcconfig */, - DCDFFA010640D140217DEAF48821ABD1 /* PostHog.release.xcconfig */, - 6F5F4E6D26C2B48557ACEBE8D608BC49 /* ResourceBundle-PostHog-PostHog-Info.plist */, - ); - name = "Support Files"; - path = "../Target Support Files/PostHog"; - sourceTree = ""; - }; - 383A41246B730B9633A2A1E341D6324B /* Pods-Cable-CableUITestsScreenshot */ = { - isa = PBXGroup; - children = ( - 00B098547C4C7CC1269E78AD6477793A /* Pods-Cable-CableUITestsScreenshot.modulemap */, - D08666942BF9AA9C3313A5D3639E9A56 /* Pods-Cable-CableUITestsScreenshot-acknowledgements.markdown */, - D5DD39201B51B24F484813D108EB3C2C /* Pods-Cable-CableUITestsScreenshot-acknowledgements.plist */, - 701F2C64F76028BD63C8BC57E763B989 /* Pods-Cable-CableUITestsScreenshot-dummy.m */, - B2E0C12DA5F3B1E2AD47FD377B6B55AD /* Pods-Cable-CableUITestsScreenshot-frameworks.sh */, - 85D33F85427449A3CF37371439A6C570 /* Pods-Cable-CableUITestsScreenshot-Info.plist */, - C94C1436FB9990B1C8458A4890A74FDD /* Pods-Cable-CableUITestsScreenshot-umbrella.h */, - 9567F7671610CAD55DFF871A5F19A42D /* Pods-Cable-CableUITestsScreenshot.debug.xcconfig */, - F6DC89C77E43B360E6ED162E84069CF6 /* Pods-Cable-CableUITestsScreenshot.release.xcconfig */, - ); - name = "Pods-Cable-CableUITestsScreenshot"; - path = "Target Support Files/Pods-Cable-CableUITestsScreenshot"; - sourceTree = ""; - }; - 6BC74FA425F6B6D9BB727803CF26F0A2 /* Products */ = { - isa = PBXGroup; - children = ( - 99F4A385A81C30B48330AC1106D13082 /* Pods-Cable */, - 5DF75AE42F75E0133CA974B0A992AF64 /* Pods-Cable-CableUITests */, - F48FB46A5EECE30CA2951FE024EEE7C6 /* Pods-Cable-CableUITestsScreenshot */, - 8F5ADA47F04EABAB81527ABBF841D2AC /* Pods-CableTests */, - 36A60AA62169A7D58D9D78E917A84BD6 /* PostHog */, - 42D87DCAF52175DC2D5C6B44419636A8 /* PostHog-PostHog */, - ); - name = Products; - sourceTree = ""; - }; - CF1408CF629C7361332E53B88F7BD30C = { - isa = PBXGroup; - children = ( - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, - D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, - DF772DFEFA18716AA6009EF1D16D2D03 /* Pods */, - 6BC74FA425F6B6D9BB727803CF26F0A2 /* Products */, - 1CA9B4AF47F09AED5B697E849F55B43E /* Targets Support Files */, - ); - sourceTree = ""; - }; - D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { - isa = PBXGroup; - children = ( - E4801F62A6B08CD9B5410329F1A18FDE /* iOS */, - ); - name = Frameworks; - sourceTree = ""; - }; - DF772DFEFA18716AA6009EF1D16D2D03 /* Pods */ = { - isa = PBXGroup; - children = ( - 0223C71E4F7A4926A35DE003CAC38985 /* PostHog */, - ); - name = Pods; - sourceTree = ""; - }; - E4801F62A6B08CD9B5410329F1A18FDE /* iOS */ = { - isa = PBXGroup; - children = ( - 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */, - ); - name = iOS; - sourceTree = ""; - }; - EB416C5BECB04A8A0EE73DFB48A2DD95 /* Pods-Cable-CableUITests */ = { - isa = PBXGroup; - children = ( - 67914AAFEBAC9A2F1F127DE54D715E79 /* Pods-Cable-CableUITests.modulemap */, - 9C442AD9B5648643BF9F8C662F4B32A7 /* Pods-Cable-CableUITests-acknowledgements.markdown */, - 0B79B981CBC521AEB9B5F19CEE6486D8 /* Pods-Cable-CableUITests-acknowledgements.plist */, - C77B0E69679F739AA5F6EF3472F81B3E /* Pods-Cable-CableUITests-dummy.m */, - 5A93DEA6097B2F04643E499901EC91A6 /* Pods-Cable-CableUITests-frameworks.sh */, - FE3276BC9DD9343402F74F0411ACBAD3 /* Pods-Cable-CableUITests-Info.plist */, - 51F57C7D4BAC17BBF066E3F6D4C4B59A /* Pods-Cable-CableUITests-umbrella.h */, - D8AAD180580FCC6EE9AEFFBE6AEDA671 /* Pods-Cable-CableUITests.debug.xcconfig */, - B1C3DCD4E3C2C0AE65F78D9A92443B29 /* Pods-Cable-CableUITests.release.xcconfig */, - ); - name = "Pods-Cable-CableUITests"; - path = "Target Support Files/Pods-Cable-CableUITests"; - sourceTree = ""; - }; - ED72A752F3C64B6966D7D367BFAE6679 /* Pods-Cable */ = { - isa = PBXGroup; - children = ( - 64AC4C5BA1754D85126569A476612795 /* Pods-Cable.modulemap */, - EF645AFD51E32C0A7B929FE3710B3EC9 /* Pods-Cable-acknowledgements.markdown */, - 39C96D15D41A39D9CA442FE17E0C0C24 /* Pods-Cable-acknowledgements.plist */, - A2428DC0690BFC906FB6BBC9517C91B8 /* Pods-Cable-dummy.m */, - 2735D5533C9516CBB656FC0E44B60D7B /* Pods-Cable-frameworks.sh */, - 5757A0552803FBE7A020E16BD23EF91B /* Pods-Cable-Info.plist */, - 3B075A62E32AE782E63CB647C2425908 /* Pods-Cable-umbrella.h */, - DD8F109A9D1262A0B31CFFE54558EF8E /* Pods-Cable.debug.xcconfig */, - DE9FD3D6919DF785A204354C43DE839D /* Pods-Cable.release.xcconfig */, - ); - name = "Pods-Cable"; - path = "Target Support Files/Pods-Cable"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 03BF60FAC1B81EED9E2FE19023955AD6 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 38E6D9105E78ED4CC0E127C62F53B331 /* Pods-Cable-CableUITests-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 09E6F859468F2F38E7B69BC67E00FB8D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 85A31731349118825F11B6BD97570A72 /* Pods-Cable-CableUITestsScreenshot-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 518F67BEE435AFD553D53E7E3449A09E /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A4351828FD656D27899119C43282A46A /* Pods-Cable-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6DBD685B887A4AF77120554981EA58B1 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - C3B88263FFF28B2C4B1C34BBFA2487CB /* ph_backward_references_enc.h in Headers */, - 308067A90C6D4714A6625855CF82A55C /* ph_bit_reader_utils.h in Headers */, - A25DB2959F2533A76503BA4FA1C3D8A8 /* ph_bit_writer_utils.h in Headers */, - 85E00607DEA0B702A88256B889A0AE46 /* ph_color_cache_utils.h in Headers */, - A881E1170145D6E36C8AD2E62BDCBB9A /* ph_common_dec.h in Headers */, - B4A5799A36D3D50E81703A45BAEE77A3 /* ph_common_sse2.h in Headers */, - 453AF00CBDEE42C069369E1F1BB482FC /* ph_common_sse41.h in Headers */, - 54E24A21DBE22F50FF2C8ED9FE8C1920 /* ph_cost_enc.h in Headers */, - 9FF301A6E7FAA2AAE00ACD5E80FD4C9B /* ph_cpu.h in Headers */, - 1ED830E9C2C477B7C3C88115DEB77BA4 /* ph_decode.h in Headers */, - E317933C3FAD82DD8D0C65EA369EF44A /* ph_dsp.h in Headers */, - 76A22346D2892A75D2C605B1E46C9C01 /* ph_encode.h in Headers */, - E6285A5C5201781908B0FA8CC012CF82 /* ph_endian_inl_utils.h in Headers */, - CA716D34DD2DC32B06C1C0B0B2C3C5C2 /* ph_filters_utils.h in Headers */, - 8A7B3562B53BEF18CBED9C9821C6A371 /* ph_format_constants.h in Headers */, - 9C95E9E96A19763B1F0B805841F064BF /* ph_histogram_enc.h in Headers */, - 0572679AE85C33C43DE798C06F3E1534 /* ph_huffman_encode_utils.h in Headers */, - 475FD727732636A7758AA528CA362744 /* ph_huffman_utils.h in Headers */, - EB51376036E66B13F5DA94861714AF5A /* ph_lossless.h in Headers */, - 83FEE6E290BF6CA38102DFB7009F6E4B /* ph_lossless_common.h in Headers */, - A086FB83D5610E0968532EF591FDC242 /* ph_mux.h in Headers */, - C40C37140BA4AC6DCF085AF14D6CBF2A /* ph_mux_types.h in Headers */, - A9947D06E0701770A3D401BD4B4DA0D1 /* ph_muxi.h in Headers */, - 4F61F0B687C035589D0FD4E29A73B0DF /* ph_neon.h in Headers */, - 144C6DD1A247AC1EDF9AD57981DCB922 /* ph_palette.h in Headers */, - E2A195E40A0C3853004F3B1C7E6ECD1C /* ph_quant.h in Headers */, - 8EC01D8A4D04C123025FD6958165F4F5 /* ph_quant_levels_utils.h in Headers */, - 395B15A53215A65E19F9C6A19D422D3A /* ph_random_utils.h in Headers */, - E617B7F0DBC07F3490AE7811551911D5 /* ph_rescaler_utils.h in Headers */, - 7460FA92770379998EFE3064CC0A9530 /* ph_sharpyuv.h in Headers */, - 4DE8C331A0B2A6959482C25ACB77E609 /* ph_sharpyuv_cpu.h in Headers */, - E7475B260DBC82214488EF1C5712B807 /* ph_sharpyuv_csp.h in Headers */, - 4EA5892CD434E01818878C81F1452F65 /* ph_sharpyuv_dsp.h in Headers */, - 3C960D07046A6486F7DC7FD20FC938FA /* ph_sharpyuv_gamma.h in Headers */, - BDE90F6901C6138EF6CB8F5ADA8F6061 /* ph_thread_utils.h in Headers */, - 923C6E3BE82D64E5623625D81D82FBA8 /* ph_types.h in Headers */, - C4A5285EB3B6FA8347156384E0693E4F /* ph_utils.h in Headers */, - ACF234EB88FF3EFA147BF0AB0A317A4D /* ph_vp8_dec.h in Headers */, - AF9FF002C9339CCCD33ADA5476D20C72 /* ph_vp8i_dec.h in Headers */, - E1A545A284C243E666880CB425BA9F20 /* ph_vp8i_enc.h in Headers */, - 9E32FD9B51892EE4CDE615D15D2FC878 /* ph_vp8li_dec.h in Headers */, - 569413C3DF6867A0AF74DE0A54B21317 /* ph_vp8li_enc.h in Headers */, - 3A184F0C63E5C112D93A58B9032A6B58 /* ph_webpi_dec.h in Headers */, - E9576BFC34B3253843CC4778948ADC07 /* ph_yuv.h in Headers */, - 9FAB5C450670E109F781D6C53E56D2AF /* PostHog.h in Headers */, - 24DEE46DD19D816066C36A6F5AFF3D20 /* PostHog-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B2D249709949995053781C643A85F693 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 36C07E870675759BC50B2A25CCC281C5 /* Pods-CableTests-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 52F449E691F258410D3E74F5BAFD41CD /* Pods-CableTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = E5E901805E2933A10C49FF4056309D27 /* Build configuration list for PBXNativeTarget "Pods-CableTests" */; - buildPhases = ( - B2D249709949995053781C643A85F693 /* Headers */, - EA62F0FCF8722BEA7EC3D4085650078A /* Sources */, - 6914A7F0DD12E15F1B99EAE8F381191A /* Frameworks */, - E7D6DA7D9DC3C67ABE6122EC8F4157A4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - FC844A347CA7E4B13AF939C15B0255D7 /* PBXTargetDependency */, - ); - name = "Pods-CableTests"; - productName = Pods_CableTests; - productReference = 8F5ADA47F04EABAB81527ABBF841D2AC /* Pods-CableTests */; - productType = "com.apple.product-type.framework"; - }; - 6835ABF5E9176D16603D3FAED02C1229 /* Pods-Cable-CableUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7F875C92A0064FEA41DD432FB3D2B9F5 /* Build configuration list for PBXNativeTarget "Pods-Cable-CableUITests" */; - buildPhases = ( - 03BF60FAC1B81EED9E2FE19023955AD6 /* Headers */, - 11B9C3062484A76F522B08C02943691A /* Sources */, - 464D3A2144371ABCE15B36C2A6A868B9 /* Frameworks */, - 72E199819E43E2956927C51BECA30F80 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - D3486A4DBC5DEF58B6DE24FC89B6DB2A /* PBXTargetDependency */, - ); - name = "Pods-Cable-CableUITests"; - productName = Pods_Cable_CableUITests; - productReference = 5DF75AE42F75E0133CA974B0A992AF64 /* Pods-Cable-CableUITests */; - productType = "com.apple.product-type.framework"; - }; - 8012054959C338A62834AD9706977FB0 /* Pods-Cable */ = { - isa = PBXNativeTarget; - buildConfigurationList = F787CC961FDD06E4AD7797CC39088604 /* Build configuration list for PBXNativeTarget "Pods-Cable" */; - buildPhases = ( - 518F67BEE435AFD553D53E7E3449A09E /* Headers */, - FAA54168123200C7B9668A497C6A5801 /* Sources */, - EA42357D9D8899D007F3BE4D5BAB8571 /* Frameworks */, - B9793F0F8CA1592CFBDA2158363BEE43 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - D061B5DE671E2844D48A74C5E35051EF /* PBXTargetDependency */, - ); - name = "Pods-Cable"; - productName = Pods_Cable; - productReference = 99F4A385A81C30B48330AC1106D13082 /* Pods-Cable */; - productType = "com.apple.product-type.framework"; - }; - 86C5E834AAC4A69D5D37D96BAF8B8330 /* Pods-Cable-CableUITestsScreenshot */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4B0A632BAB21EA6EFE88E8B7CF12F513 /* Build configuration list for PBXNativeTarget "Pods-Cable-CableUITestsScreenshot" */; - buildPhases = ( - 09E6F859468F2F38E7B69BC67E00FB8D /* Headers */, - C77F8BDE60E6E297A97F81D0D98F9ACB /* Sources */, - 3890B9438DD6516F1EA5E3CAACCE8FFE /* Frameworks */, - 01BFA279DAE745D11102F6375A2AA1AD /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 732576E2FD2FBF077D0F21C041585F88 /* PBXTargetDependency */, - ); - name = "Pods-Cable-CableUITestsScreenshot"; - productName = Pods_Cable_CableUITestsScreenshot; - productReference = F48FB46A5EECE30CA2951FE024EEE7C6 /* Pods-Cable-CableUITestsScreenshot */; - productType = "com.apple.product-type.framework"; - }; - 8879D5F28A55518ACFB247594F87F75A /* PostHog */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3EA171DF69C6597F9BFFD3F4672C5A70 /* Build configuration list for PBXNativeTarget "PostHog" */; - buildPhases = ( - 6DBD685B887A4AF77120554981EA58B1 /* Headers */, - 9E80A9B36A41B08E0176D40615787A6F /* Sources */, - A26CDBEA01200B96B68F314398EEE38C /* Frameworks */, - 185F1A104E0899D7A77085F3320B8948 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 723D01296E2FD4600B1D936335ABA7C3 /* PBXTargetDependency */, - ); - name = PostHog; - productName = PostHog; - productReference = 36A60AA62169A7D58D9D78E917A84BD6 /* PostHog */; - productType = "com.apple.product-type.framework"; - }; - E326EE08AE4CF9FA8C947B96B6F8AB07 /* PostHog-PostHog */ = { - isa = PBXNativeTarget; - buildConfigurationList = CE81BF29562F4A5FB5765C0AAFFE4A53 /* Build configuration list for PBXNativeTarget "PostHog-PostHog" */; - buildPhases = ( - 97346091A7C7DDCF5BE5DA549CF27240 /* Sources */, - 73D22CAADE4FCD69C0A715C06D35F9B8 /* Frameworks */, - A32586E3BC3E86EF8B3D882DD9339221 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "PostHog-PostHog"; - productName = PostHog; - productReference = 42D87DCAF52175DC2D5C6B44419636A8 /* PostHog-PostHog */; - productType = "com.apple.product-type.bundle"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BFDFE7DC352907FC980B868725387E98 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1600; - LastUpgradeCheck = 1600; - }; - buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; - compatibilityVersion = "Xcode 16.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - Base, - en, - ); - mainGroup = CF1408CF629C7361332E53B88F7BD30C; - minimizedProjectReferenceProxies = 0; - preferredProjectObjectVersion = 77; - productRefGroup = 6BC74FA425F6B6D9BB727803CF26F0A2 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8012054959C338A62834AD9706977FB0 /* Pods-Cable */, - 6835ABF5E9176D16603D3FAED02C1229 /* Pods-Cable-CableUITests */, - 86C5E834AAC4A69D5D37D96BAF8B8330 /* Pods-Cable-CableUITestsScreenshot */, - 52F449E691F258410D3E74F5BAFD41CD /* Pods-CableTests */, - 8879D5F28A55518ACFB247594F87F75A /* PostHog */, - E326EE08AE4CF9FA8C947B96B6F8AB07 /* PostHog-PostHog */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 01BFA279DAE745D11102F6375A2AA1AD /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 185F1A104E0899D7A77085F3320B8948 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 15F7984FF736D8AF019F417DA8901036 /* PostHog-PostHog in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 72E199819E43E2956927C51BECA30F80 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A32586E3BC3E86EF8B3D882DD9339221 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4805E842E48C745A9E6A4C06D4731373 /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B9793F0F8CA1592CFBDA2158363BEE43 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - E7D6DA7D9DC3C67ABE6122EC8F4157A4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 11B9C3062484A76F522B08C02943691A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7241253A695D3360662518FABF8A18F9 /* Pods-Cable-CableUITests-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97346091A7C7DDCF5BE5DA549CF27240 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 9E80A9B36A41B08E0176D40615787A6F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4238E88AE8BA8624075729D326C0F4A9 /* alpha_enc.c in Sources */, - D76EBEDD6E19898C194F1FADF177664C /* alpha_processing.c in Sources */, - 3155F7F3D2B67132C14891B5A2E8F65B /* alpha_processing_neon.c in Sources */, - DFA1A5698507154A30CAA31FD6BD1A85 /* alpha_processing_sse2.c in Sources */, - 81A20054B797FF0F766333BE4913DBCC /* alpha_processing_sse41.c in Sources */, - DA6006155C3D0397BC53C971B6764235 /* analysis_enc.c in Sources */, - 43DBAA9AC5E2E63021F065F66FCBC065 /* ApplicationEventPublisher.swift in Sources */, - 6979FEA56C2EE86990E4473D5F0085CF /* ApplicationLifecyclePublisher.swift in Sources */, - 8494528FA0360A1CAFFAC147C9849375 /* ApplicationScreenViewPublisher.swift in Sources */, - 7E015D3260F0BFD29E3F0690B1E59E0D /* ApplicationViewLayoutPublisher.swift in Sources */, - 2468C99062231929D4FA5A274E16CB72 /* AssociatedKeys.swift in Sources */, - 51557FD624B2D8D939BD15D6D67DAA9F /* AutocaptureEventProcessing.swift in Sources */, - F632525798590D31F88730CB5B08A136 /* backward_references_cost_enc.c in Sources */, - 01C82EF77CAD48D420201E5041F71981 /* backward_references_enc.c in Sources */, - 5C603F51B177ED3F49DA5B71B25253F4 /* bit_reader_utils.c in Sources */, - 4391AA0D63D56F3F6233972901D35B62 /* bit_writer_utils.c in Sources */, - 526DF460F26A30E9A4C72AE94B898DAC /* BottomSection.swift in Sources */, - B1F2CAAAD6999BC7C09AE72FB869D703 /* CGColor+Util.swift in Sources */, - 493D47C4E0ADF0AA3C30D3FB6C2F9C57 /* CGSize+Util.swift in Sources */, - C2D9AAF9E55A505AAEA9DDC14B0BED48 /* color_cache_utils.c in Sources */, - 97F67C732B893F315EFDD5D598C3F71E /* config_enc.c in Sources */, - 6B8EC9F9C886019474D30A2A38FF68B2 /* ConfirmationMessage.swift in Sources */, - 9ADEA2121D351229E95318A512FB3E0F /* cost.c in Sources */, - C8B7CAFDAF0B31D48040478F71C0EBE2 /* cost_enc.c in Sources */, - F8222B205BE0E6FCCBE3ACE42940599A /* cost_neon.c in Sources */, - F395CAD4C36A94E525379C352F11ADF4 /* cost_sse2.c in Sources */, - 5C24BD9E95BD5410843BD372C028EF2F /* cpu.c in Sources */, - A2111B640365DEB1D1262861FB526BB6 /* Data+Gzip.swift in Sources */, - 8753E838E3006A1E2F72DF5F6AC2C67A /* Date+Util.swift in Sources */, - 52D4C546D7A305CC6D9B29EBBE6BA5DA /* DateUtils.swift in Sources */, - EC0A7D763F31745069FD68B9549C9A9A /* dec.c in Sources */, - BD645B55581F18C45D3A077C7120F169 /* dec_clip_tables.c in Sources */, - 858173DFD0E9446CB773D1CBB00418BD /* dec_neon.c in Sources */, - 1089A5E22CB40877785F1B4D5B8337D8 /* dec_sse2.c in Sources */, - 0357CEDE60497FDDDFF4206D50D84D32 /* dec_sse41.c in Sources */, - 51F949044932E2740E23BC026B3CB070 /* DI.swift in Sources */, - CDCFCFCE3B5007033F6B1E3AD1C1545E /* DictUtils.swift in Sources */, - 06F57CD4670CD1DAE91524790BFC6EC1 /* EdgeBorder.swift in Sources */, - 64C0E1A516D383EA3EC23A7BFC448FCE /* EmojiRating.swift in Sources */, - 96B479CE48E29ED5239A38806540AFA6 /* enc.c in Sources */, - 0B03D2F73EAB1910FB04EFA201F7FA59 /* enc_neon.c in Sources */, - C38E5112CB2F46733703F641350364B7 /* enc_sse2.c in Sources */, - 77EB7B90D005993B65D9AD7A2533B13D /* enc_sse41.c in Sources */, - 627DC06DB4D42F2FB9605AF95A528471 /* Errors.swift in Sources */, - CE53F772FBCCABD2B04587B2BD4FD177 /* FileUtils.swift in Sources */, - 89D364405A06C6C8B06DA285AC3E5A5F /* filter_enc.c in Sources */, - AD26200A6DEF1516F1C192768115BC55 /* filters.c in Sources */, - D246F10610362DEE01F847DA6180E08C /* filters_neon.c in Sources */, - 9E27DE50AA66E7023C06263FB25B71AE /* filters_sse2.c in Sources */, - 65FEDD05D8424912681558FCA1154C7D /* filters_utils.c in Sources */, - C085F172DD546D7A954F9E7CFA6549AF /* Float+Util.swift in Sources */, - 8B95C63A2E2B72090AE5A5AB08AA2F26 /* ForwardingPickerViewDelegate.swift in Sources */, - E641EDF95A76D284807990177906C9C7 /* frame_enc.c in Sources */, - 273B9F6A4399EA87F762570917E52D34 /* Hedgelog.swift in Sources */, - D16B60C05E3B60104BB3BD49495D0879 /* histogram_enc.c in Sources */, - 50B1BD836429AB01BD316C26265CD71C /* huffman_encode_utils.c in Sources */, - 5A6216FF081F6510212318F7654826F2 /* huffman_utils.c in Sources */, - 7FDBA2F04F5750B61E6DF8FA25F849BA /* iterator_enc.c in Sources */, - 38AF18D906A0862245B23AA744DEFE31 /* lossless.c in Sources */, - 550CB5E2CAB7E6F88A94256F8D4E1AD0 /* lossless_enc.c in Sources */, - 9DFA8122176106229CC767FEAF4B3304 /* lossless_enc_neon.c in Sources */, - EB6484265BE63031CDDCB790CD983769 /* lossless_enc_sse2.c in Sources */, - 9B7B7D98445B36EC45CFE01B99DC85AB /* lossless_enc_sse41.c in Sources */, - 4E33D63DE814B6144A257A9CBE8B695C /* lossless_neon.c in Sources */, - 790E4BF42B672B3EA4E21B66CB0DB05B /* lossless_sse2.c in Sources */, - A49FA4C1772B8C68D42DD34E69C76134 /* lossless_sse41.c in Sources */, - A23F680D118045C38E24C79BCA87F3F8 /* MethodSwizzler.swift in Sources */, - 889631E71D504EF98E0F26B2E5E45DD9 /* MultipleChoiceOptions.swift in Sources */, - EF9115BBB1E249A72211587F17B96835 /* muxedit.c in Sources */, - 6899E938FC1259735FECEDCCBD5DCB12 /* muxinternal.c in Sources */, - D4A6686C68814AF2106A2CAB329530DB /* muxread.c in Sources */, - 183E1F870F4FDDEA7C57FEC6BD70ED5B /* near_lossless_enc.c in Sources */, - 74B9900ECC81F957E50D152BBBED824E /* NetworkSample.swift in Sources */, - 9C0BAA41861364D4FF410FA385BA695D /* NumberRating.swift in Sources */, - 30C93CCDE28363680E50EA3A91FE8B0A /* Optional+Util.swift in Sources */, - 48B8B5B751C3E55843A697C3792C7837 /* palette.c in Sources */, - 4E1473B9FF22300F21A2ABCA3911A03B /* picture_csp_enc.c in Sources */, - 7597FE04F73E19981054D053749CBC7C /* picture_enc.c in Sources */, - 8E6CE9ACB1DEA1CB2ED4E8C43A6CB1DF /* picture_psnr_enc.c in Sources */, - 450E0503FAA2B07BD4D1F2C3CB11B122 /* picture_rescale_enc.c in Sources */, - C20E16F51EE280913ECD5C8939075863 /* picture_tools_enc.c in Sources */, - 5CC284C9FBACD9B6424307C1E8EE2618 /* PostHog-dummy.m in Sources */, - 16053DB437B9398112A24A047C55D124 /* PostHogApi.swift in Sources */, - C10F91F1C306F86A8DE647E9392EAD5B /* PostHogAppLifeCycleIntegration.swift in Sources */, - 93F25171D356A06B938299A8DC1B01A3 /* PostHogAutocaptureEventTracker.swift in Sources */, - 74A2933F21E1826BFCE98767611A1F2B /* PostHogAutocaptureIntegration.swift in Sources */, - 18BF9FAC9C9D784C748A0AFEE36F20E1 /* PostHogBatchUploadInfo.swift in Sources */, - 0F409AE2D56C546B8A1155D37CCA80D5 /* PostHogConfig.swift in Sources */, - 538F223AE0B8D0CA9A7DFF6C1BCDF61A /* PostHogConsoleLogInterceptor.swift in Sources */, - 95D55CBE1ACFF50FD1EBC6D4936B2E94 /* PostHogConsumerPayload.swift in Sources */, - 32BD682BFDA6241281CEBA7904C5A5A1 /* PostHogContext.swift in Sources */, - A958EABDF4F3CF594A315F89E9E5CB16 /* PostHogDisplaySurvey.swift in Sources */, - E2BB8174AB27B7FC6D610F3D6D2E55C9 /* PostHogDisplaySurveyAppearance.swift in Sources */, - 13D9AE4C00CE2535E0DEF135306CB8BF /* PostHogDisplaySurveyQuestion.swift in Sources */, - F149A1F7278934A1B4C094EBF060D2B6 /* PostHogEvent.swift in Sources */, - 438E9D989048FFC3DF68443837BF6398 /* PostHogExtensions.swift in Sources */, - 7DA49ED9EF36CCDB27E476FFFC6D5660 /* PostHogFileBackedQueue.swift in Sources */, - BFF5E04D16488F3C122C6F2099D16702 /* PostHogIntegration.swift in Sources */, - F64FD409AECB0B56E3D10EC2B87A36EE /* PostHogLegacyQueue.swift in Sources */, - E68C5CF478D5211B525ABCD7A11F346A /* PostHogLogEntry.swift in Sources */, - 252C54D4211DE3406B82DC8206E354DA /* PostHogLogLevel.swift in Sources */, - AC8C4D813E979741EB84B24C111118F0 /* PostHogMaskViewModifier.swift in Sources */, - 944E6DAB32560E951BAE16F5157F0D35 /* PostHogNextSurveyQuestion.swift in Sources */, - FDDD950364CB85A70790993AD79EFB17 /* PostHogNoMaskViewModifier.swift in Sources */, - 0BE2AAAE30AE1B29B23B1E55C819D7A7 /* PostHogPersonProfiles.swift in Sources */, - 9C1AF5AC07E7ED9CCAFECF84C27E73BD /* PostHogPropertiesSanitizer.swift in Sources */, - 5F8EF5FE4D73E4F038E2DEA4D5C6A146 /* PostHogQueue.swift in Sources */, - 2B18064A9F0190519F509DD5891B81A0 /* PostHogRemoteConfig.swift in Sources */, - 152739ADAFEA0909584A96C79E07C151 /* PostHogReplayIntegration.swift in Sources */, - 570EB0858F1AFE99A8B7CA45C6C3BAEE /* PostHogScreenViewIntegration.swift in Sources */, - 38177A6BBDA55B9E965610C950A24204 /* PostHogSDK.swift in Sources */, - 3E4221A803DEDC8750348BA20A791EEE /* PostHogSessionManager.swift in Sources */, - 9748FF5103EE1045677BDAED5F00DFC9 /* PostHogSessionReplayConfig.swift in Sources */, - B959C06622003BB1462ABA53313D422B /* PostHogSessionReplayConsoleLogConfig.swift in Sources */, - 81A74112BDB883335465416F2DDD4F73 /* PostHogSessionReplayConsoleLogsPlugin.swift in Sources */, - 8CC38A2ED3E279B2AFF414782AF6EF05 /* PostHogSessionReplayNetworkPlugin.swift in Sources */, - 13F0B4871E8EB793200587DF5C1C4246 /* PostHogSessionReplayPlugin.swift in Sources */, - 580FDEB8A8A152A39A502A40A58724C2 /* PostHogStorage.swift in Sources */, - 615D7CAA8FFEFF59CC50B16C3981EBE4 /* PostHogStorageManager.swift in Sources */, - 6045482673A7946DF31DA4DE7CBC35F6 /* PostHogSurvey.swift in Sources */, - 6DCDBF153AE370C69768E7A7B57F0748 /* PostHogSurvey+Display.swift in Sources */, - E9F821F9B181137CF9756E7B5DB290F8 /* PostHogSurveyAppearance.swift in Sources */, - 947857B02E90224ED7E680233305DB5E /* PostHogSurveyConditions.swift in Sources */, - 73FCA47CCEAFE7E4B070B112F21A9C7E /* PostHogSurveyEnums.swift in Sources */, - 7206DFEB5BB955754C39FC17011FF545 /* PostHogSurveyIntegration.swift in Sources */, - A1C65033EA26B7634A850B9635138B45 /* PostHogSurveyQuestion.swift in Sources */, - 2599E595E56867C2CC371352E74E5CB0 /* PostHogSurveyResponse.swift in Sources */, - 847080B8D7683BBBEF894EE6EA38912E /* PostHogSurveysConfig.swift in Sources */, - 560F19C3A96258BB787771F53438277C /* PostHogSurveysDefaultDelegate.swift in Sources */, - 76A53A0EB8A7F9C73557B3B32D669BBB /* PostHogSwiftUIViewModifiers.swift in Sources */, - 822476AFB7FFB4F8605DC4FA8A19D926 /* PostHogSwizzler.swift in Sources */, - 01EB398AE7FB8D1AA43CCC4BE25256A1 /* PostHogTagViewModifier.swift in Sources */, - BF00726324688B38AB498AB8C0D6A9CC /* PostHogVersion.swift in Sources */, - 2C34DA75C8E0FD9A4A61AF56826CE149 /* predictor_enc.c in Sources */, - 91A181721A95245C3A19DD7DF8D3ED20 /* quant_enc.c in Sources */, - 918561BED43D6D9A55D7ED87A4233FAA /* quant_levels_dec_utils.c in Sources */, - 900853FFC431D50BA4A0E6B015873734 /* quant_levels_utils.c in Sources */, - 1C54E6F4D075181AE6E195DC2CBA737D /* QuestionHeader.swift in Sources */, - E645C1A88AD4148D5B873D3811EA3BE0 /* QuestionTypes.swift in Sources */, - 0E16356A19CF2C4ED7FE29BBB4F700BC /* random_utils.c in Sources */, - 9B3694B60BA51B86814BC4B1C0AADE57 /* Reachability.swift in Sources */, - 125C8B61C19411B4E222E2D4670EDA7C /* ReadWriteLock.swift in Sources */, - 80DA5FB9AFF890ABE611B5A044CDB036 /* rescaler.c in Sources */, - 07169E6F462DE20D301856B85FB0C74F /* rescaler_neon.c in Sources */, - 2C4358D6D544B725F9B1EB1A3FF6D35B /* rescaler_sse2.c in Sources */, - 6D1C160E221E65E31F2C2C1FB79BF4B3 /* rescaler_utils.c in Sources */, - CA4046A0C463B283FDEF14E88E615622 /* Resources.swift in Sources */, - 0867541398BCAD5D8D499F66BB8887FB /* RRStyle.swift in Sources */, - B94F9615E1406645D9976C740A12F52E /* RRWireframe.swift in Sources */, - E3C0C764F23ABB7818F31999FE8856BF /* SegmentedControl.swift in Sources */, - 3491FB1468B98D7D43A3D900A0B00281 /* sharpyuv.c in Sources */, - 39FD34007D0F0D009C7F332C7DC5E187 /* sharpyuv_cpu.c in Sources */, - 360ABBEBC1B15FDCF4DA237E7647F2BE /* sharpyuv_csp.c in Sources */, - 6CD6C7D137F96E4B377097CC311E07A9 /* sharpyuv_dsp.c in Sources */, - C41DEDAF518B62C012E82A92168E2F71 /* sharpyuv_gamma.c in Sources */, - 4ED6FC8835C4F8D158F713E6D7E8BB76 /* sharpyuv_neon.c in Sources */, - C9FDB67918170E2DD508541BA216560E /* sharpyuv_sse2.c in Sources */, - CCEB5CA997D8A5E48BD7E73B6F4EB71D /* ssim.c in Sources */, - 62B8676E90C8E48016693783C0036D8F /* ssim_sse2.c in Sources */, - 5E514467B3F4FB8469C113819DE25FF2 /* String+Util.swift in Sources */, - 40BD75D3EE82B288BCAA04D3DACEBD99 /* Survey+Util.swift in Sources */, - 4A2589F3E16BE0902709FF2A3630BE17 /* SurveyButton.swift in Sources */, - D4AB0395E8CB9D1D7677DAE14B6B5F59 /* SurveyDisplayController.swift in Sources */, - C1D7ABBD432849EC886D9852B1A06CC4 /* SurveyPresentationDetentsRepresentable.swift in Sources */, - 44058A91D10EDA3415018DE7925C348B /* SurveySheet.swift in Sources */, - DF22F1DD5882402BECE6B91CD1A227E8 /* SurveysRootView.swift in Sources */, - 8D7AFBBCADCA7780494700C5EE031A7A /* SurveysWindow.swift in Sources */, - D9BF8B39F7A90185A2AE69B33D1F91C2 /* SwiftUI+Util.swift in Sources */, - E6E78E2A322D4E7736D55ADCE1D73BF6 /* syntax_enc.c in Sources */, - 8A2EA1FD1DE278407880880388CC7863 /* thread_utils.c in Sources */, - 645BFE97A3A8E993F08B87285951AB82 /* TimeBasedEpochGenerator.swift in Sources */, - 4F5F04A349551F176FFCF2A5F318055D /* token_enc.c in Sources */, - 0C4DEC31440C7333BC97E7B78D67BC72 /* tree_enc.c in Sources */, - A87193AFE5CEF7AA673BC80BA4FB9C9B /* UIApplication+.swift in Sources */, - 9C1C48333A30CF28A6F8F918216615FB /* UIColor+Util.swift in Sources */, - 0E3BEE1E611D26371DAF50C23E7AC1BB /* UIImage+Util.swift in Sources */, - 4BB3C8C41EA289FB29DCEC068F7FCD76 /* UIImage+WebP.swift in Sources */, - 19D82E01983174C6A45C7EE85AA81795 /* UITextInputTraits+Util.swift in Sources */, - 4FA4C53C4992B0098AACCF9805B840B3 /* UIView+PostHogLabel.swift in Sources */, - B511EA85DAC18FD6205B26B71DE1050C /* UIView+Util.swift in Sources */, - 89AB8E555A39C1D168505C89C8CF5777 /* UIViewController.swift in Sources */, - 236BD01A81BCD619F470D17D00FA2440 /* UIWindow+.swift in Sources */, - 287E75C60B7FB8A9A9E6A4D5E2465701 /* upsampling.c in Sources */, - A7E4B6A307B6D11D3216D24DFF540A76 /* upsampling_neon.c in Sources */, - 932C39898E846B7343F58C87A7C8A18D /* upsampling_sse2.c in Sources */, - A923905603728FF1F5D63FA3383A7647 /* upsampling_sse41.c in Sources */, - D5AC0B15E162B74F89BE5C08EAD54621 /* URLSessionExtension.swift in Sources */, - 32AECF33E41A6D0971F70B1220A182F5 /* URLSessionInterceptor.swift in Sources */, - 3E0A9BA7670824E8A542BB0238965308 /* URLSessionSwizzler.swift in Sources */, - 7203FB71B522DF771D5BF42CFE86CE87 /* utils.c in Sources */, - 3B1D530F1C85B1227913F0026BFD0BDC /* UUIDUtils.swift in Sources */, - 0AE01E9EDF9EC4A863872FB71F99983B /* View+PostHogLabel.swift in Sources */, - 866CAB0B3150FEF773928C86AD9171E1 /* ViewTreeSnapshotStatus.swift in Sources */, - EBDF53B9DD3C2058CE110560E30904D6 /* vp8l_enc.c in Sources */, - 9DAE58DDF04BB8AC6700C178740CA4A2 /* webp_enc.c in Sources */, - CEEA0DE4B861387A64E05F48B5A7CEE5 /* yuv.c in Sources */, - 0EF0A53B84657EF4A4A6C6E11C409DF3 /* yuv_neon.c in Sources */, - 82AAEEC1837B795CD013636BAEF14DA9 /* yuv_sse2.c in Sources */, - 0A24C32E44D0415EF08780849822BCEE /* yuv_sse41.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C77F8BDE60E6E297A97F81D0D98F9ACB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 141DFED4583A49BE8122CF85D9C530B2 /* Pods-Cable-CableUITestsScreenshot-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EA62F0FCF8722BEA7EC3D4085650078A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7A4857DA3F9C646CCC878F9329D19E60 /* Pods-CableTests-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - FAA54168123200C7B9668A497C6A5801 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B9342CDA79BCBDBEAB3D05BB11EDDBFD /* Pods-Cable-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 723D01296E2FD4600B1D936335ABA7C3 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "PostHog-PostHog"; - target = E326EE08AE4CF9FA8C947B96B6F8AB07 /* PostHog-PostHog */; - targetProxy = F71674A1D40932D753945F87EC1C5BB1 /* PBXContainerItemProxy */; - }; - 732576E2FD2FBF077D0F21C041585F88 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = PostHog; - target = 8879D5F28A55518ACFB247594F87F75A /* PostHog */; - targetProxy = 0D8CAE93284D0305C41A4E72F6A07403 /* PBXContainerItemProxy */; - }; - D061B5DE671E2844D48A74C5E35051EF /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = PostHog; - target = 8879D5F28A55518ACFB247594F87F75A /* PostHog */; - targetProxy = BF702A62A6C910D491628C2736B02269 /* PBXContainerItemProxy */; - }; - D3486A4DBC5DEF58B6DE24FC89B6DB2A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = PostHog; - target = 8879D5F28A55518ACFB247594F87F75A /* PostHog */; - targetProxy = 62E12AA42617D0B8EC3B5959449CF6A7 /* PBXContainerItemProxy */; - }; - FC844A347CA7E4B13AF939C15B0255D7 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "Pods-Cable"; - target = 8012054959C338A62834AD9706977FB0 /* Pods-Cable */; - targetProxy = 61238D0FC731DB2BBAA0E8FC812EACC8 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 066BC42F1D3638A5C6146DB138A5E4C8 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DD8F109A9D1262A0B31CFFE54558EF8E /* Pods-Cable.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - INFOPLIST_FILE = "Target Support Files/Pods-Cable/Pods-Cable-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Cable/Pods-Cable.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 24B82364850CD61A83AC39AB834AB0C5 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DA9D1BD5EFA6A8CA76078ADFBB131D2D /* PostHog.debug.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREFIX_HEADER = "Target Support Files/PostHog/PostHog-prefix.pch"; - GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = "Target Support Files/PostHog/PostHog-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/PostHog/PostHog.modulemap"; - PRODUCT_MODULE_NAME = PostHog; - PRODUCT_NAME = PostHog; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_INSTALL_OBJC_HEADER = YES; - SWIFT_VERSION = 5.3; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 2790FD60DE7909879F2E49B80999A418 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DE9FD3D6919DF785A204354C43DE839D /* Pods-Cable.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - INFOPLIST_FILE = "Target Support Files/Pods-Cable/Pods-Cable-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Cable/Pods-Cable.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 393E6A9D498B59105CC15A51FD45FA2F /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DCDFFA010640D140217DEAF48821ABD1 /* PostHog.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREFIX_HEADER = "Target Support Files/PostHog/PostHog-prefix.pch"; - GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = "Target Support Files/PostHog/PostHog-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/PostHog/PostHog.modulemap"; - PRODUCT_MODULE_NAME = PostHog; - PRODUCT_NAME = PostHog; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_INSTALL_OBJC_HEADER = YES; - SWIFT_VERSION = 5.3; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 5814376CBA6AD9C93845A394E4A7DDCD /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DA9D1BD5EFA6A8CA76078ADFBB131D2D /* PostHog.debug.xcconfig */; - buildSettings = { - CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/PostHog"; - IBSC_MODULE = PostHog; - INFOPLIST_FILE = "Target Support Files/PostHog/ResourceBundle-PostHog-PostHog-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - PRODUCT_NAME = PostHog; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Debug; - }; - 5F5AE8F49433A59A9392F2677BEB6317 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D8AAD180580FCC6EE9AEFFBE6AEDA671 /* Pods-Cable-CableUITests.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - INFOPLIST_FILE = "Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 6C71F7B4D69110348654FDB3B0D188E2 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = F6DC89C77E43B360E6ED162E84069CF6 /* Pods-Cable-CableUITestsScreenshot.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - INFOPLIST_FILE = "Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 7B9BF991C60D017C403B3AB25A3D9D21 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = B1C3DCD4E3C2C0AE65F78D9A92443B29 /* Pods-Cable-CableUITests.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - INFOPLIST_FILE = "Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 7D418BF305962BD394B69D9080B4BFFA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DCDFFA010640D140217DEAF48821ABD1 /* PostHog.release.xcconfig */; - buildSettings = { - CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/PostHog"; - IBSC_MODULE = PostHog; - INFOPLIST_FILE = "Target Support Files/PostHog/ResourceBundle-PostHog-PostHog-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - PRODUCT_NAME = PostHog; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - 8C9963745FD01B6B3EF95F9CE58D9F10 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_DEBUG=1", - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Debug; - }; - 92492A978D4AC63ABB84F6977386188B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9567F7671610CAD55DFF871A5F19A42D /* Pods-Cable-CableUITestsScreenshot.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - INFOPLIST_FILE = "Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 97EC72F28306B21CC7F052446D53670C /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9833327F3E1576D80701526ECD9F245B /* Pods-CableTests.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - INFOPLIST_FILE = "Target Support Files/Pods-CableTests/Pods-CableTests-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-CableTests/Pods-CableTests.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - B9F3557045385CA606F5AF548D58B58A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_RELEASE=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Release; - }; - CB70384C563F6BB137294A0E0712EF1C /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 54A7FFEE9A3DE4A9543516D400145E56 /* Pods-CableTests.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - INFOPLIST_FILE = "Target Support Files/Pods-CableTests/Pods-CableTests-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-CableTests/Pods-CableTests.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 3EA171DF69C6597F9BFFD3F4672C5A70 /* Build configuration list for PBXNativeTarget "PostHog" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 24B82364850CD61A83AC39AB834AB0C5 /* Debug */, - 393E6A9D498B59105CC15A51FD45FA2F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 8C9963745FD01B6B3EF95F9CE58D9F10 /* Debug */, - B9F3557045385CA606F5AF548D58B58A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4B0A632BAB21EA6EFE88E8B7CF12F513 /* Build configuration list for PBXNativeTarget "Pods-Cable-CableUITestsScreenshot" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 92492A978D4AC63ABB84F6977386188B /* Debug */, - 6C71F7B4D69110348654FDB3B0D188E2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 7F875C92A0064FEA41DD432FB3D2B9F5 /* Build configuration list for PBXNativeTarget "Pods-Cable-CableUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 5F5AE8F49433A59A9392F2677BEB6317 /* Debug */, - 7B9BF991C60D017C403B3AB25A3D9D21 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - CE81BF29562F4A5FB5765C0AAFFE4A53 /* Build configuration list for PBXNativeTarget "PostHog-PostHog" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 5814376CBA6AD9C93845A394E4A7DDCD /* Debug */, - 7D418BF305962BD394B69D9080B4BFFA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - E5E901805E2933A10C49FF4056309D27 /* Build configuration list for PBXNativeTarget "Pods-CableTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97EC72F28306B21CC7F052446D53670C /* Debug */, - CB70384C563F6BB137294A0E0712EF1C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F787CC961FDD06E4AD7797CC39088604 /* Build configuration list for PBXNativeTarget "Pods-Cable" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 066BC42F1D3638A5C6146DB138A5E4C8 /* Debug */, - 2790FD60DE7909879F2E49B80999A418 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; -} diff --git a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable-CableUITests.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable-CableUITests.xcscheme deleted file mode 100644 index 77eaac9..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable-CableUITests.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable-CableUITestsScreenshot.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable-CableUITestsScreenshot.xcscheme deleted file mode 100644 index 303c7af..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable-CableUITestsScreenshot.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable.xcscheme deleted file mode 100644 index 59c1334..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-Cable.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-CableTests.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-CableTests.xcscheme deleted file mode 100644 index a5eb9e0..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/Pods-CableTests.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/PostHog-PostHog.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/PostHog-PostHog.xcscheme deleted file mode 100644 index 1f38c86..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/PostHog-PostHog.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/PostHog.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/PostHog.xcscheme deleted file mode 100644 index fc0d05a..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/PostHog.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 55a472f..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/lange-hegermann.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,41 +0,0 @@ - - - - - SchemeUserState - - Pods-Cable-CableUITests.xcscheme - - isShown - - - Pods-Cable-CableUITestsScreenshot.xcscheme - - isShown - - - Pods-Cable.xcscheme - - isShown - - - Pods-CableTests.xcscheme - - isShown - - - PostHog-PostHog.xcscheme - - isShown - - - PostHog.xcscheme - - isShown - - - - SuppressBuildableAutocreation - - - diff --git a/Pods/PostHog/LICENSE b/Pods/PostHog/LICENSE deleted file mode 100644 index 7e799e3..0000000 --- a/Pods/PostHog/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) [2023] [PostHog] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/Pods/PostHog/PostHog/App Life Cycle/ApplicationLifecyclePublisher.swift b/Pods/PostHog/PostHog/App Life Cycle/ApplicationLifecyclePublisher.swift deleted file mode 100644 index 987fe65..0000000 --- a/Pods/PostHog/PostHog/App Life Cycle/ApplicationLifecyclePublisher.swift +++ /dev/null @@ -1,199 +0,0 @@ -// -// ApplicationLifecyclePublisher.swift -// PostHog -// -// Created by Yiannis Josephides on 16/12/2024. -// - -#if os(iOS) || os(tvOS) || os(visionOS) - import UIKit -#elseif os(macOS) - import AppKit -#elseif os(watchOS) - import WatchKit -#endif - -typealias AppLifecycleHandler = () -> Void - -protocol AppLifecyclePublishing: AnyObject { - /// Registers a callback for the `didBecomeActive` event. - func onDidBecomeActive(_ callback: @escaping AppLifecycleHandler) -> RegistrationToken - /// Registers a callback for the `didEnterBackground` event. - func onDidEnterBackground(_ callback: @escaping AppLifecycleHandler) -> RegistrationToken - /// Registers a callback for the `didFinishLaunching` event. - func onDidFinishLaunching(_ callback: @escaping AppLifecycleHandler) -> RegistrationToken -} - -/** - A publisher that handles application lifecycle events and allows registering callbacks for them. - - This class provides a way to observe application lifecycle events like when the app becomes active, - enters background, or finishes launching. Callbacks can be registered for each event type and will - be automatically unregistered when their registration token is deallocated. - - Example usage: - ``` - let token = ApplicationLifecyclePublisher.shared.onDidBecomeActive { - // App became active logic - } - // Keep `token` in memory to keep the registration active - // When token is deallocated, the callback will be automatically unregistered - ``` - */ -final class ApplicationLifecyclePublisher: BaseApplicationLifecyclePublisher { - /// Shared instance to allow easy access across the app. - static let shared = ApplicationLifecyclePublisher() - - override private init() { - super.init() - - let defaultCenter = NotificationCenter.default - - #if os(iOS) || os(tvOS) - defaultCenter.addObserver(self, - selector: #selector(appDidFinishLaunching), - name: UIApplication.didFinishLaunchingNotification, - object: nil) - defaultCenter.addObserver(self, - selector: #selector(appDidEnterBackground), - name: UIApplication.didEnterBackgroundNotification, - object: nil) - defaultCenter.addObserver(self, - selector: #selector(appDidBecomeActive), - name: UIApplication.didBecomeActiveNotification, - object: nil) - #elseif os(visionOS) - defaultCenter.addObserver(self, - selector: #selector(appDidFinishLaunching), - name: UIApplication.didFinishLaunchingNotification, - object: nil) - defaultCenter.addObserver(self, - selector: #selector(appDidEnterBackground), - name: UIScene.willDeactivateNotification, - object: nil) - defaultCenter.addObserver(self, - selector: #selector(appDidBecomeActive), - name: UIScene.didActivateNotification, - object: nil) - #elseif os(macOS) - defaultCenter.addObserver(self, - selector: #selector(appDidFinishLaunching), - name: NSApplication.didFinishLaunchingNotification, - object: nil) - // macOS does not have didEnterBackgroundNotification, so we use didResignActiveNotification - defaultCenter.addObserver(self, - selector: #selector(appDidEnterBackground), - name: NSApplication.didResignActiveNotification, - object: nil) - defaultCenter.addObserver(self, - selector: #selector(appDidBecomeActive), - name: NSApplication.didBecomeActiveNotification, - object: nil) - #elseif os(watchOS) - if #available(watchOS 7.0, *) { - NotificationCenter.default.addObserver(self, - selector: #selector(appDidBecomeActive), - name: WKApplication.didBecomeActiveNotification, - object: nil) - } else { - NotificationCenter.default.addObserver(self, - selector: #selector(appDidBecomeActive), - name: .init("UIApplicationDidBecomeActiveNotification"), - object: nil) - } - #endif - } - - // MARK: - Handlers - - @objc private func appDidEnterBackground() { - notifyHandlers(didEnterBackgroundHandlers) - } - - @objc private func appDidBecomeActive() { - notifyHandlers(didBecomeActiveHandlers) - } - - @objc private func appDidFinishLaunching() { - notifyHandlers(didFinishLaunchingHandlers) - } - - private func notifyHandlers(_ handlers: [AppLifecycleHandler]) { - for handler in handlers { - notifyHander(handler) - } - } - - private func notifyHander(_ handler: @escaping AppLifecycleHandler) { - if Thread.isMainThread { - handler() - } else { - DispatchQueue.main.async(execute: handler) - } - } -} - -class BaseApplicationLifecyclePublisher: AppLifecyclePublishing { - private let registrationLock = NSLock() - - private var didBecomeActiveCallbacks: [UUID: AppLifecycleHandler] = [:] - private var didEnterBackgroundCallbacks: [UUID: AppLifecycleHandler] = [:] - private var didFinishLaunchingCallbacks: [UUID: AppLifecycleHandler] = [:] - - var didBecomeActiveHandlers: [AppLifecycleHandler] { - registrationLock.withLock { Array(didBecomeActiveCallbacks.values) } - } - - var didEnterBackgroundHandlers: [AppLifecycleHandler] { - registrationLock.withLock { Array(didEnterBackgroundCallbacks.values) } - } - - var didFinishLaunchingHandlers: [AppLifecycleHandler] { - registrationLock.withLock { Array(didFinishLaunchingCallbacks.values) } - } - - /// Registers a callback for the `didBecomeActive` event. - func onDidBecomeActive(_ callback: @escaping AppLifecycleHandler) -> RegistrationToken { - register(handler: callback, on: \.didBecomeActiveCallbacks) - } - - /// Registers a callback for the `didEnterBackground` event. - func onDidEnterBackground(_ callback: @escaping AppLifecycleHandler) -> RegistrationToken { - register(handler: callback, on: \.didEnterBackgroundCallbacks) - } - - /// Registers a callback for the `didFinishLaunching` event. - func onDidFinishLaunching(_ callback: @escaping AppLifecycleHandler) -> RegistrationToken { - register(handler: callback, on: \.didFinishLaunchingCallbacks) - } - - func register( - handler callback: @escaping AppLifecycleHandler, - on keyPath: ReferenceWritableKeyPath - ) -> RegistrationToken { - let id = UUID() - registrationLock.withLock { - self[keyPath: keyPath][id] = callback - } - - return RegistrationToken { [weak self] in - // Registration token deallocated here - guard let self else { return } - self.registrationLock.withLock { - self[keyPath: keyPath][id] = nil - } - } - } -} - -final class RegistrationToken { - private let onDealloc: () -> Void - - init(_ onDealloc: @escaping () -> Void) { - self.onDealloc = onDealloc - } - - deinit { - onDealloc() - } -} diff --git a/Pods/PostHog/PostHog/App Life Cycle/PostHogAppLifeCycleIntegration.swift b/Pods/PostHog/PostHog/App Life Cycle/PostHogAppLifeCycleIntegration.swift deleted file mode 100644 index b423326..0000000 --- a/Pods/PostHog/PostHog/App Life Cycle/PostHogAppLifeCycleIntegration.swift +++ /dev/null @@ -1,214 +0,0 @@ -// -// PostHogAppLifeCycleIntegration.swift -// PostHog -// -// Created by Ioannis Josephides on 19/02/2025. -// - -import Foundation - -/** - Add capability to capture application lifecycle events. - - This integration: - - captures an `App Installed` event on the first launch of the app - - captures an `App Updated` event on any subsequent launch with a different version - - captures an `App Opened` event when the app is opened (including the first launch) - - captures an `App Backgrounded` event when the app moves to the background - */ -final class PostHogAppLifeCycleIntegration: PostHogIntegration { - var requiresSwizzling: Bool { false } - - private static var integrationInstalledLock = NSLock() - private static var integrationInstalled = false - private static var didCaptureAppInstallOrUpdate = false - - private weak var postHog: PostHogSDK? - - // True if the app is launched for the first time - private var isFreshAppLaunch = true - // Manually maintained flag to determine background status of the app - private var isAppBackgrounded: Bool = true - - private var didBecomeActiveToken: RegistrationToken? - private var didEnterBackgroundToken: RegistrationToken? - private var didFinishLaunchingToken: RegistrationToken? - - func install(_ postHog: PostHogSDK) throws { - try PostHogAppLifeCycleIntegration.integrationInstalledLock.withLock { - if PostHogAppLifeCycleIntegration.integrationInstalled { - throw InternalPostHogError(description: "App life cycle integration already installed to another PostHogSDK instance.") - } - PostHogAppLifeCycleIntegration.integrationInstalled = true - } - - self.postHog = postHog - - start() - captureAppInstallOrUpdated() - } - - func uninstall(_ postHog: PostHogSDK) { - // uninstall only for integration instance - if self.postHog === postHog || self.postHog == nil { - stop() - self.postHog = nil - PostHogAppLifeCycleIntegration.integrationInstalledLock.withLock { - PostHogAppLifeCycleIntegration.integrationInstalled = false - } - } - } - - /** - Start capturing app lifecycles events - */ - func start() { - let publisher = DI.main.appLifecyclePublisher - didFinishLaunchingToken = publisher.onDidFinishLaunching { [weak self] in - self?.captureAppInstallOrUpdated() - } - didBecomeActiveToken = publisher.onDidBecomeActive { [weak self] in - self?.captureAppOpened() - } - didEnterBackgroundToken = publisher.onDidEnterBackground { [weak self] in - self?.captureAppBackgrounded() - } - } - - /** - Stop capturing app lifecycle events - */ - func stop() { - didFinishLaunchingToken = nil - didBecomeActiveToken = nil - didEnterBackgroundToken = nil - } - - private func captureAppInstallOrUpdated() { - // Check if Application Installed or Application Updated was already checked in the lifecycle of this app - // This can be called multiple times in case of optOut, multiple instances or start/stop integration - guard let postHog, !PostHogAppLifeCycleIntegration.didCaptureAppInstallOrUpdate else { return } - - PostHogAppLifeCycleIntegration.didCaptureAppInstallOrUpdate = true - - if !postHog.config.captureApplicationLifecycleEvents { - hedgeLog("Skipping Application Installed/Application Updated event - captureApplicationLifecycleEvents is disabled in configuration") - return - } - - let bundle = Bundle.main - - let versionName = bundle.infoDictionary?["CFBundleShortVersionString"] as? String - let versionCode = bundle.infoDictionary?["CFBundleVersion"] as? String - - // capture app installed/updated - let userDefaults = UserDefaults.standard - - let previousVersion = userDefaults.string(forKey: "PHGVersionKey") - let previousVersionCode = userDefaults.string(forKey: "PHGBuildKeyV2") - - var props: [String: Any] = [:] - var event: String - if previousVersionCode == nil { - // installed - event = "Application Installed" - } else { - event = "Application Updated" - - // Do not send version updates if its the same - if previousVersionCode == versionCode { - return - } - - if previousVersion != nil { - props["previous_version"] = previousVersion - } - props["previous_build"] = previousVersionCode - } - - var syncDefaults = false - if versionName != nil { - props["version"] = versionName - userDefaults.setValue(versionName, forKey: "PHGVersionKey") - syncDefaults = true - } - - if versionCode != nil { - props["build"] = versionCode - userDefaults.setValue(versionCode, forKey: "PHGBuildKeyV2") - syncDefaults = true - } - - if syncDefaults { - userDefaults.synchronize() - } - - postHog.capture(event, properties: props) - } - - private func captureAppOpened() { - guard let postHog else { return } - - guard isAppBackgrounded else { - hedgeLog("Skipping Application Opened event - app already in foreground") - return - } - - isAppBackgrounded = false - - if !postHog.config.captureApplicationLifecycleEvents { - hedgeLog("Skipping Application Opened event - captureApplicationLifecycleEvents is disabled in configuration") - return - } - - var props: [String: Any] = [:] - props["from_background"] = !isFreshAppLaunch - - if isFreshAppLaunch { - let bundle = Bundle.main - - let versionName = bundle.infoDictionary?["CFBundleShortVersionString"] as? String - let versionCode = bundle.infoDictionary?["CFBundleVersion"] as? String - - if versionName != nil { - props["version"] = versionName - } - if versionCode != nil { - props["build"] = versionCode - } - - isFreshAppLaunch = false - } - - postHog.capture("Application Opened", properties: props) - } - - private func captureAppBackgrounded() { - guard let postHog else { return } - - guard !isAppBackgrounded else { - hedgeLog("Skipping Application Opened event - app already in background") - return - } - - isAppBackgrounded = true - - if !postHog.config.captureApplicationLifecycleEvents { - hedgeLog("Skipping Application Backgrounded event - captureApplicationLifecycleEvents is disabled in configuration") - return - } - - postHog.capture("Application Backgrounded") - } -} - -#if TESTING - extension PostHogAppLifeCycleIntegration { - static func clearInstalls() { - PostHogAppLifeCycleIntegration.didCaptureAppInstallOrUpdate = false - integrationInstalledLock.withLock { - integrationInstalled = false - } - } - } -#endif diff --git a/Pods/PostHog/PostHog/ApplicationViewLayoutPublisher.swift b/Pods/PostHog/PostHog/ApplicationViewLayoutPublisher.swift deleted file mode 100644 index 7855a3f..0000000 --- a/Pods/PostHog/PostHog/ApplicationViewLayoutPublisher.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// ApplicationViewLayoutPublisher.swift -// PostHog -// -// Created by Ioannis Josephides on 19/03/2025. -// - -#if os(iOS) || os(tvOS) - import UIKit - - typealias ApplicationViewLayoutHandler = () -> Void - - protocol ViewLayoutPublishing: AnyObject { - /// Registers a callback for getting notified when a UIView is laid out. - /// Note: callback guaranteed to be called on main thread - func onViewLayout(throttle: TimeInterval, _ callback: @escaping ApplicationViewLayoutHandler) -> RegistrationToken - } - - final class ApplicationViewLayoutPublisher: BaseApplicationViewLayoutPublisher { - static let shared = ApplicationViewLayoutPublisher() - - private var hasSwizzled: Bool = false - - func start() { - swizzleLayoutSubviews() - } - - func stop() { - unswizzleLayoutSubviews() - } - - func swizzleLayoutSubviews() { - guard !hasSwizzled else { return } - hasSwizzled = true - - swizzle( - forClass: UIView.self, - original: #selector(UIView.layoutSublayers(of:)), - new: #selector(UIView.ph_swizzled_layoutSublayers(of:)) - ) - } - - func unswizzleLayoutSubviews() { - guard hasSwizzled else { return } - hasSwizzled = false - - // swizzling twice will exchange implementations back to original - swizzle( - forClass: UIView.self, - original: #selector(UIView.layoutSublayers(of:)), - new: #selector(UIView.ph_swizzled_layoutSublayers(of:)) - ) - } - - override func onViewLayout(throttle interval: TimeInterval, _ callback: @escaping ApplicationViewLayoutHandler) -> RegistrationToken { - let id = UUID() - registrationLock.withLock { - self.onViewLayoutCallbacks[id] = ThrottledHandler(handler: callback, interval: interval) - } - - // start on first callback registration - if !hasSwizzled { - start() - } - - return RegistrationToken { [weak self] in - // Registration token deallocated here - guard let self else { return } - let handlerCount = self.registrationLock.withLock { - self.onViewLayoutCallbacks[id] = nil - return self.onViewLayoutCallbacks.values.count - } - - // stop when there are no more callbacks - if handlerCount <= 0 { - self.stop() - } - } - } - - // Called from swizzled `UIView.layoutSubviews` - fileprivate func layoutSubviews() { - notifyHandlers() - } - - #if TESTING - func simulateLayoutSubviews() { - layoutSubviews() - } - #endif - } - - class BaseApplicationViewLayoutPublisher: ViewLayoutPublishing { - fileprivate let registrationLock = NSLock() - - var onViewLayoutCallbacks: [UUID: ThrottledHandler] = [:] - - final class ThrottledHandler { - static let throttleQueue = DispatchQueue(label: "com.posthog.ThrottledHandler", - target: .global(qos: .utility)) - - let interval: TimeInterval - let handler: ApplicationViewLayoutHandler - - private var lastFired: Date = .distantPast - - init(handler: @escaping ApplicationViewLayoutHandler, interval: TimeInterval) { - self.handler = handler - self.interval = interval - } - - func throttleHandler() { - let now = now() - let timeSinceLastFired = now.timeIntervalSince(lastFired) - - if timeSinceLastFired >= interval { - lastFired = now - // notify on main - DispatchQueue.main.async(execute: handler) - } - } - } - - func onViewLayout(throttle interval: TimeInterval, _ callback: @escaping ApplicationViewLayoutHandler) -> RegistrationToken { - let id = UUID() - registrationLock.withLock { - self.onViewLayoutCallbacks[id] = ThrottledHandler( - handler: callback, - interval: interval - ) - } - - return RegistrationToken { [weak self] in - // Registration token deallocated here - guard let self else { return } - self.registrationLock.withLock { - self.onViewLayoutCallbacks[id] = nil - } - } - } - - func notifyHandlers() { - ThrottledHandler.throttleQueue.async { - // Don't lock on main - let handlers = self.registrationLock.withLock { self.onViewLayoutCallbacks.values } - for handler in handlers { - handler.throttleHandler() - } - } - } - } - - extension UIView { - @objc func ph_swizzled_layoutSublayers(of layer: CALayer) { - ph_swizzled_layoutSublayers(of: layer) // call original, not altering execution logic - ApplicationViewLayoutPublisher.shared.layoutSubviews() - } - } -#endif diff --git a/Pods/PostHog/PostHog/Autocapture/AutocaptureEventProcessing.swift b/Pods/PostHog/PostHog/Autocapture/AutocaptureEventProcessing.swift deleted file mode 100644 index 121db12..0000000 --- a/Pods/PostHog/PostHog/Autocapture/AutocaptureEventProcessing.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// AutocaptureEventProcessing.swift -// PostHog -// -// Created by Yiannis Josephides on 30/10/2024. -// - -#if os(iOS) || targetEnvironment(macCatalyst) - import Foundation - - protocol AutocaptureEventProcessing: AnyObject { - func process(source: PostHogAutocaptureEventTracker.EventSource, event: PostHogAutocaptureEventTracker.EventData) - } -#endif diff --git a/Pods/PostHog/PostHog/Autocapture/ForwardingPickerViewDelegate.swift b/Pods/PostHog/PostHog/Autocapture/ForwardingPickerViewDelegate.swift deleted file mode 100644 index 771ce61..0000000 --- a/Pods/PostHog/PostHog/Autocapture/ForwardingPickerViewDelegate.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// ForwardingPickerViewDelegate.swift -// PostHog -// -// Created by Yiannis Josephides on 24/10/2024. -// - -#if os(iOS) || targetEnvironment(macCatalyst) - import UIKit - - final class ForwardingPickerViewDelegate: NSObject, UIPickerViewDelegate, UIPickerViewDataSource { - // this needs to be weak since `actualDelegate` will hold a strong reference to `ForwardingPickerViewDelegate` - weak var actualDelegate: UIPickerViewDelegate? - private var valueChangedCallback: (() -> Void)? - - // We respond to the same selectors that the original delegate responds to - override func responds(to aSelector: Selector!) -> Bool { - actualDelegate?.responds(to: aSelector) ?? false - } - - init(delegate: UIPickerViewDelegate?, onValueChanged: @escaping () -> Void) { - actualDelegate = delegate - valueChangedCallback = onValueChanged - } - - // MARK: - UIPickerViewDataSource - - func numberOfComponents(in pickerView: UIPickerView) -> Int { - (actualDelegate as? UIPickerViewDataSource)?.numberOfComponents(in: pickerView) ?? 0 - } - - func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { - (actualDelegate as? UIPickerViewDataSource)?.pickerView(pickerView, numberOfRowsInComponent: component) ?? 0 - } - - // MARK: - UIPickerViewDelegate - - func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { - valueChangedCallback?() - actualDelegate?.pickerView?(pickerView, didSelectRow: row, inComponent: component) - } - - func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { - actualDelegate?.pickerView?(pickerView, viewForRow: row, forComponent: component, reusing: view) ?? UIView() - } - - func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat { - actualDelegate?.pickerView?(pickerView, widthForComponent: component) ?? .zero - } - - func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { - actualDelegate?.pickerView?(pickerView, rowHeightForComponent: component) ?? .zero - } - - func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { - actualDelegate?.pickerView?(pickerView, titleForRow: row, forComponent: component) - } - - func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? { - actualDelegate?.pickerView?(pickerView, attributedTitleForRow: row, forComponent: component) - } - } - - extension UIPickerViewDelegate { - var ph_forwardingDelegate: UIPickerViewDelegate? { - get { objc_getAssociatedObject(self, &AssociatedKeys.phForwardingDelegate) as? UIPickerViewDelegate } - set { objc_setAssociatedObject(self, &AssociatedKeys.phForwardingDelegate, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - } - -#endif diff --git a/Pods/PostHog/PostHog/Autocapture/PostHogAutocaptureEventTracker.swift b/Pods/PostHog/PostHog/Autocapture/PostHogAutocaptureEventTracker.swift deleted file mode 100644 index bc38f0c..0000000 --- a/Pods/PostHog/PostHog/Autocapture/PostHogAutocaptureEventTracker.swift +++ /dev/null @@ -1,606 +0,0 @@ -// -// PostHogAutocaptureEventTracker.swift -// PostHog -// -// Created by Yiannis Josephides on 14/10/2024. -// - -#if os(iOS) || targetEnvironment(macCatalyst) - import UIKit - - class PostHogAutocaptureEventTracker { - struct EventData { - let touchCoordinates: CGPoint? - let value: String? - let screenName: String? - let viewHierarchy: [Element] - // values >0 means that this event will be debounced for `debounceInterval` - let debounceInterval: TimeInterval - } - - struct Element { - let text: String - let targetClass: String - let baseClass: String? - let label: String? - - var elementsChainEntry: String { - var attributes = [String]() - - if !text.isEmpty { - attributes.append("text=\(text.quoted)") - } - if let baseClass, !baseClass.isEmpty { - attributes.append("attr__class=\(baseClass.quoted)") - } - if let label, !label.isEmpty { - attributes.append("attr_id=\(label.quoted)") - } - - return attributes.isEmpty ? targetClass : "\(targetClass):\(attributes.joined())" - } - } - - enum EventSource { - case notification(name: String) - case actionMethod(description: String) - case gestureRecognizer(description: String) - } - - static var eventProcessor: (any AutocaptureEventProcessing)? { - willSet { - if newValue != nil { - swizzle() - } else { - unswizzle() - } - } - } - - private static var hasSwizzled: Bool = false - private static func swizzle() { - guard !hasSwizzled else { return } - hasSwizzled = true - swizzleMethods() - registerNotifications() - } - - private static func unswizzle() { - guard hasSwizzled else { return } - hasSwizzled = false - swizzleMethods() // swizzling again will exchange implementations back to original - unregisterNotifications() - } - - private static func swizzleMethods() { - PostHog.swizzle( - forClass: UIApplication.self, - original: #selector(UIApplication.sendAction), - new: #selector(UIApplication.ph_swizzled_uiapplication_sendAction) - ) - - PostHog.swizzle( - forClass: UIGestureRecognizer.self, - original: #selector(setter: UIGestureRecognizer.state), - new: #selector(UIGestureRecognizer.ph_swizzled_uigesturerecognizer_state_Setter) - ) - - PostHog.swizzle( - forClass: UIScrollView.self, - original: #selector(setter: UIScrollView.contentOffset), - new: #selector(UIScrollView.ph_swizzled_setContentOffset_Setter) - ) - - PostHog.swizzle( - forClass: UIPickerView.self, - original: #selector(setter: UIPickerView.delegate), - new: #selector(UIPickerView.ph_swizzled_setDelegate) - ) - } - - private static func registerNotifications() { - NotificationCenter.default.addObserver( - PostHogAutocaptureEventTracker.self, - selector: #selector(didEndEditing), - name: UITextField.textDidEndEditingNotification, - object: nil - ) - NotificationCenter.default.addObserver( - PostHogAutocaptureEventTracker.self, - selector: #selector(didEndEditing), - name: UITextView.textDidEndEditingNotification, - object: nil - ) - } - - private static func unregisterNotifications() { - NotificationCenter.default.removeObserver(PostHogAutocaptureEventTracker.self, name: UITextField.textDidEndEditingNotification, object: nil) - NotificationCenter.default.removeObserver(PostHogAutocaptureEventTracker.self, name: UITextView.textDidEndEditingNotification, object: nil) - } - - // `UITextField` or `UITextView` did end editing notification - @objc static func didEndEditing(_ notification: NSNotification) { - guard let view = notification.object as? UIView, let eventData = view.eventData else { return } - - eventProcessor?.process(source: .notification(name: "change"), event: eventData) - } - } - - extension UIApplication { - @objc func ph_swizzled_uiapplication_sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool { - defer { - // Currently, the action methods pointing to a SwiftUI target are blocked. - let targetClass = String(describing: object_getClassName(target)) - if targetClass.contains("SwiftUI") { - hedgeLog("Action methods on SwiftUI targets are not yet supported.") - } else if let control = sender as? UIControl, - control.ph_shouldTrack(action, for: target), - let eventData = control.eventData, - let eventDescription = control.event(for: action, to: target)?.description(forControl: control) - { - PostHogAutocaptureEventTracker.eventProcessor?.process(source: .actionMethod(description: eventDescription), event: eventData) - } - } - - // first, call original method - return ph_swizzled_uiapplication_sendAction(action, to: target, from: sender, for: event) - } - } - - extension UIGestureRecognizer { - // swiftlint:disable:next cyclomatic_complexity - @objc func ph_swizzled_uigesturerecognizer_state_Setter(_ state: UIGestureRecognizer.State) { - // first, call original method - ph_swizzled_uigesturerecognizer_state_Setter(state) - - guard state == .ended, let view, shouldTrack(view) else { return } - - // block scroll and zoom gestures for `UIScrollView`. - if let scrollView = view as? UIScrollView { - if self === scrollView.panGestureRecognizer { - return - } - #if !os(tvOS) - if self === scrollView.pinchGestureRecognizer { - return - } - #endif - } - - // block all gestures for `UISwitch` (already captured via `.valueChanged` action) - if String(describing: type(of: view)).starts(with: "UISwitch") { - return - } - // ignore gestures in `UIPickerColumnView` - if String(describing: type(of: view)) == "UIPickerColumnView" { - return - } - - let gestureDescription: String? - switch self { - case is UITapGestureRecognizer: - gestureDescription = EventType.kTouch - case is UISwipeGestureRecognizer: - gestureDescription = EventType.kSwipe - case is UIPanGestureRecognizer: - gestureDescription = EventType.kPan - case is UILongPressGestureRecognizer: - gestureDescription = EventType.kLongPress - #if !os(tvOS) - case is UIPinchGestureRecognizer: - gestureDescription = EventType.kPinch - case is UIRotationGestureRecognizer: - gestureDescription = EventType.kRotation - case is UIScreenEdgePanGestureRecognizer: - gestureDescription = EventType.kPan - #endif - default: - gestureDescription = nil - } - - guard let gestureDescription else { return } - - if let eventData = view.eventData { - PostHogAutocaptureEventTracker.eventProcessor?.process(source: .gestureRecognizer(description: gestureDescription), event: eventData) - } - } - } - - extension UIScrollView { - @objc func ph_swizzled_setContentOffset_Setter(_ newContentOffset: CGPoint) { - // first, call original method - ph_swizzled_setContentOffset_Setter(newContentOffset) - - guard shouldTrack(self) else { - return - } - - // ignore all keyboard events - if let window, window.isKeyboardWindow { - return - } - - // scrollview did not scroll (contentOffset didn't change) - guard contentOffset != newContentOffset else { - return - } - - // block scrolls on UIPickerTableView. (captured via a forwarding delegate implementation) - if String(describing: type(of: self)) == "UIPickerTableView" { - return - } - - if let eventData { - PostHogAutocaptureEventTracker.eventProcessor?.process(source: .gestureRecognizer(description: EventType.kScroll), event: eventData) - } - } - } - - extension UIPickerView { - @objc func ph_swizzled_setDelegate(_ delegate: (any UIPickerViewDelegate)?) { - guard let delegate else { - // this just removes the delegate - return ph_swizzled_setDelegate(delegate) - } - - // if delegate doesn't respond to this selector, then we can't intercept selection changes - guard delegate.responds(to: #selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:))) else { - return ph_swizzled_setDelegate(delegate) - } - - // wrap in a forwarding delegate so we can intercept calls - let forwardingDelegate = ForwardingPickerViewDelegate(delegate: delegate) { [weak self] in - if let data = self?.eventData { - PostHogAutocaptureEventTracker.eventProcessor?.process(source: .gestureRecognizer(description: EventType.kValueChange), event: data) - } - } - - // Need to keep a strong reference to keep this forwarding delegate instance alive - delegate.ph_forwardingDelegate = forwardingDelegate - - // call original setter - ph_swizzled_setDelegate(forwardingDelegate) - } - } - - extension UIView { - var eventData: PostHogAutocaptureEventTracker.EventData? { - guard shouldTrack(self) else { return nil } - return PostHogAutocaptureEventTracker.EventData( - touchCoordinates: nil, - value: ph_autocaptureText - .map(sanitizeText), - screenName: nearestViewController - .flatMap(UIViewController.ph_topViewController) - .flatMap(UIViewController.getViewControllerName), - viewHierarchy: sequence(first: self, next: \.superview) - .map(\.toElement), - debounceInterval: ph_autocaptureDebounceInterval - ) - } - } - - private extension UIView { - var toElement: PostHogAutocaptureEventTracker.Element { - PostHogAutocaptureEventTracker.Element( - text: ph_autocaptureText.map(sanitizeText) ?? "", - targetClass: descriptiveTypeName, - baseClass: baseTypeName, - label: postHogLabel - ) - } - } - - extension UIControl { - func event(for action: Selector, to target: Any?) -> UIControl.Event? { - var events: [UIControl.Event] = [ - .valueChanged, - .touchDown, - .touchDownRepeat, - .touchDragInside, - .touchDragOutside, - .touchDragEnter, - .touchDragExit, - .touchUpInside, - .touchUpOutside, - .touchCancel, - .editingDidBegin, - .editingChanged, - .editingDidEnd, - .editingDidEndOnExit, - .primaryActionTriggered, - ] - - if #available(iOS 14.0, tvOS 14.0, macCatalyst 14.0, *) { - events.append(.menuActionTriggered) - } - - // latest event for action - return events.first { event in - self.actions(forTarget: target, forControlEvent: event)?.contains(action.description) ?? false - } - } - } - - extension UIControl.Event { - // swiftlint:disable:next cyclomatic_complexity - func description(forControl control: UIControl) -> String? { - if self == .primaryActionTriggered { - if control is UIButton { - return EventType.kTouch // UIButton triggers primaryAction with a touch interaction - } else if control is UISegmentedControl { - return EventType.kValueChange // UISegmentedControl changes its value - } else if control is UITextField { - return EventType.kSubmit // UITextField uses this for submit-like behavior - } else if control is UISwitch { - return EventType.kToggle - } else if control is UIDatePicker { - return EventType.kValueChange - } else if control is UIStepper { - return EventType.kValueChange - } else { - return EventType.kPrimaryAction - } - } - - // General event descriptions - if UIControl.Event.allTouchEvents.contains(self) { - return EventType.kTouch - } else if UIControl.Event.allEditingEvents.contains(self) { - return EventType.kChange - } else if self == .valueChanged { - if control is UISwitch { - // toggle better describes a value chagne in a switch control - return EventType.kToggle - } - return EventType.kValueChange - } else if #available(iOS 14.0, tvOS 14.0, macCatalyst 14.0, *), self == .menuActionTriggered { - return EventType.kMenuAction - } - - return nil - } - } - - extension UIViewController { - class func ph_topViewController(base: UIViewController? = UIApplication.getCurrentWindow()?.rootViewController) -> UIViewController? { - if let nav = base as? UINavigationController { - return ph_topViewController(base: nav.visibleViewController) - - } else if let tab = base as? UITabBarController, let selected = tab.selectedViewController { - return ph_topViewController(base: selected) - - } else if let presented = base?.presentedViewController { - return ph_topViewController(base: presented) - } - return base - } - } - - extension UIResponder { - var nearestViewController: UIViewController? { - self as? UIViewController ?? next?.nearestViewController - } - } - - private func typeName(of type: AnyClass) -> String { - let typeName = String(describing: type) - if let match = typeName.range(of: "^[^<]+", options: .regularExpression) { - // Extracts everything before the first '<' to deal with generics - return String(typeName[match]) - } - return typeName - } - - // common base types in UIKit that should not be captured - private let excludedBaseTypes: [AnyClass] = [ - NSObject.self, - UIResponder.self, - UIControl.self, - UIView.self, - UIScrollView.self, - ] - - extension NSObject { - var descriptiveTypeName: String { - typeName(of: type(of: self)) - } - - var baseTypeName: String? { - guard - let superclass = type(of: self).superclass(), - !excludedBaseTypes.contains(where: { $0 == superclass }) - else { - return nil - } - return typeName(of: superclass) - } - } - - protocol AutoCapturable { - var ph_autocaptureText: String? { get } - var ph_autocaptureEvents: UIControl.Event { get } - var ph_autocaptureDebounceInterval: TimeInterval { get } - func ph_shouldTrack(_ action: Selector, for target: Any?) -> Bool - } - - extension UIView: AutoCapturable { - @objc var ph_autocaptureEvents: UIControl.Event { .touchUpInside } - @objc var ph_autocaptureText: String? { nil } - @objc var ph_autocaptureDebounceInterval: TimeInterval { 0 } - @objc func ph_shouldTrack(_: Selector, for _: Any?) -> Bool { - false // by default views are not tracked. Can be overridden in subclasses - } - } - - extension UIButton { - override var ph_autocaptureText: String? { title(for: .normal) ?? title(for: .selected) } - } - - extension UIControl { - @objc override func ph_shouldTrack(_ action: Selector, for target: Any?) -> Bool { - guard shouldTrack(self) else { return false } - return actions(forTarget: target, forControlEvent: ph_autocaptureEvents)?.contains(action.description) ?? false - } - } - - extension UIScrollView { - override var ph_autocaptureDebounceInterval: TimeInterval { 0.4 } - } - - extension UISegmentedControl { - override var ph_autocaptureEvents: UIControl.Event { .valueChanged } - override var ph_autocaptureText: String? { - // -1 if no segment is selected - if (0 ..< numberOfSegments) ~= selectedSegmentIndex { - return titleForSegment(at: selectedSegmentIndex) - } - return nil - } - } - - extension UIPageControl { - override var ph_autocaptureEvents: UIControl.Event { .valueChanged } - } - - extension UISearchBar { - override var ph_autocaptureEvents: UIControl.Event { .editingDidEnd } - } - - extension UIToolbar { - override var ph_autocaptureEvents: UIControl.Event { - if #available(iOS 14.0, *) { .menuActionTriggered } else { .primaryActionTriggered } - } - } - - extension UITextField { - override var ph_autocaptureText: String? { text ?? attributedText?.string ?? placeholder } - override func ph_shouldTrack(_: Selector, for _: Any?) -> Bool { - // Just making sure that in the future we don't intercept UIControl.Ecent (even though it's not currently emited) - // Tracked via `UITextField.textDidEndEditingNotification` - false - } - } - - extension UITextView { - override var ph_autocaptureText: String? { text ?? attributedText?.string } - override func ph_shouldTrack(_: Selector, for _: Any?) -> Bool { - shouldTrack(self) - } - } - - extension UIStepper { - override var ph_autocaptureEvents: UIControl.Event { .valueChanged } - override var ph_autocaptureText: String? { "\(value)" } - } - - extension UISlider { - override var ph_autocaptureDebounceInterval: TimeInterval { 0.3 } - override var ph_autocaptureEvents: UIControl.Event { .valueChanged } - override var ph_autocaptureText: String? { "\(value)" } - } - - extension UISwitch { - @objc override var ph_autocaptureEvents: UIControl.Event { .valueChanged } - override var ph_autocaptureText: String? { "\(isOn)" } - } - - extension UIPickerView { - override var ph_autocaptureText: String? { - (0 ..< numberOfComponents).reduce("") { result, component in - // -1 if no row is selected - let selectedRow = selectedRow(inComponent: component) - let rowCount = numberOfRows(inComponent: component) - - if (0 ..< rowCount) ~= selectedRow { - if let title = delegate?.pickerView?(self, titleForRow: selectedRow, forComponent: component) { - return result.isEmpty ? title : "\(result) \(title)" - } else if let title = delegate?.pickerView?(self, attributedTitleForRow: selectedRow, forComponent: component) { - return result.isEmpty ? title.string : "\(result) \(title.string)" - } - } - - return result - } - } - } - - #if !os(tvOS) - extension UIDatePicker { - override var ph_autocaptureEvents: UIControl.Event { .valueChanged } - } - #endif - - private func shouldTrack(_ view: UIView) -> Bool { - if view.isHidden { return false } - if !view.isUserInteractionEnabled { return false } - if view.isNoCapture() { return false } - if view.window?.isKeyboardWindow == true { return false } - - if let textField = view as? UITextField, textField.isSensitiveText() { - return false - } - if let textView = view as? UITextView, textView.isSensitiveText() { - return false - } - - // check view hierarchy up - if let superview = view.superview { - return shouldTrack(superview) - } - - return true - } - - // TODO: Filter out or obfuscate strings that look like sensitive data - // see: https://github.com/PostHog/posthog-js/blob/0cfffcac9bdf1da3fbb9478c1a51170a325bd57f/src/autocapture-utils.ts#L389 - private func sanitizeText(_ title: String) -> String { - title - .trimmingCharacters(in: .whitespacesAndNewlines) // trim - .replacingOccurrences( // sequence of spaces, returns and line breaks - of: "[ \\r\\n]+", - with: " ", - options: .regularExpression - ) - .replacingOccurrences( // sanitize zero-width unicode characters - of: "[\\u{200B}\\u{200C}\\u{200D}\\u{FEFF}]", - with: "", - options: .regularExpression - ) - .limit(to: 255) - } - - enum EventType { - static let kValueChange = "value_changed" - static let kSubmit = "submit" - static let kToggle = "toggle" - static let kPrimaryAction = "primary_action" - static let kMenuAction = "menu_action" - static let kChange = "change" - - static let kTouch = "touch" - static let kSwipe = "swipe" - static let kPinch = "pinch" - static let kPan = "pan" - static let kScroll = "scroll" - static let kRotation = "rotation" - static let kLongPress = "long_press" - } - - // MARK: - Helpers - - private extension String { - func limit(to length: Int) -> String { - if count > length { - let index = index(startIndex, offsetBy: length) - return String(self[.. 0 { - debounceTimers[eventHash]?.invalidate() // Keep cancelling existing - debounceTimers[eventHash] = Timer.scheduledTimer(withTimeInterval: event.debounceInterval, repeats: false) { [weak self] _ in - self?.handleEventProcessing(source: source, event: event) - self?.debounceTimers.removeValue(forKey: eventHash) // Clean up once fired - } - } else { - handleEventProcessing(source: source, event: event) - } - } - - /** - Handles the processing of autocapture events by extracting event details, building properties, and sending them to PostHog. - - - Parameters: - - source: The source of the event (action method, gesture, or notification). Values are already mapped to `$event_type` earlier in the chain - - event: The event data including view hierarchy, screen name, and other metadata. - - This function extracts event details such as the event type, view hierarchy, and touch coordinates. - It creates a structured payload with relevant properties (e.g., tag_name, elements, element_chain) and sends it to the - associated PostHog instance for further processing. - */ - private func handleEventProcessing(source: PostHogAutocaptureEventTracker.EventSource, event: PostHogAutocaptureEventTracker.EventData) { - guard let postHog else { - return - } - - let eventType: String = switch source { - case let .actionMethod(description): description - case let .gestureRecognizer(description): description - case let .notification(name): name - } - - var properties: [String: Any] = [:] - - if let screenName = event.screenName { - properties["$screen_name"] = screenName - } - - let elementsChain = event.viewHierarchy - .map(\.elementsChainEntry) - .joined(separator: elementsChainDelimiter) - - if let coordinates = event.touchCoordinates { - properties["$touch_x"] = coordinates.x - properties["$touch_y"] = coordinates.y - } - - postHog.autocapture( - eventType: eventType, - elementsChain: elementsChain, - properties: properties - ) - } - } - - #if TESTING - extension PostHogAutocaptureIntegration { - static func clearInstalls() { - integrationInstalledLock.withLock { - integrationInstalled = false - } - } - } - #endif -#endif diff --git a/Pods/PostHog/PostHog/Autocapture/SwiftUI/View+PostHogLabel.swift b/Pods/PostHog/PostHog/Autocapture/SwiftUI/View+PostHogLabel.swift deleted file mode 100644 index 262308e..0000000 --- a/Pods/PostHog/PostHog/Autocapture/SwiftUI/View+PostHogLabel.swift +++ /dev/null @@ -1,156 +0,0 @@ -// -// View+PostHogLabel.swift -// PostHog -// -// Created by Yiannis Josephides on 04/12/2024. -// - -#if os(iOS) || targetEnvironment(macCatalyst) - import SwiftUI - - public extension View { - /** - Adds a custom label to this view for use with PostHog's auto-capture functionality. - - By setting a custom label, you can easily identify and filter interactions with this specific element in your analytics data. - - ### Usage - ```swift - struct ContentView: View { - var body: some View { - Button("Login") { - ... - } - .postHogLabel("loginButton") - } - } - ``` - - - Parameter label: A custom label that uniquely identifies the element for analytics purposes. - */ - func postHogLabel(_ label: String?) -> some View { - modifier(PostHogLabelTaggerViewModifier(label: label)) - } - } - - private struct PostHogLabelTaggerViewModifier: ViewModifier { - let label: String? - - func body(content: Content) -> some View { - content - .background(viewTagger) - } - - @ViewBuilder - private var viewTagger: some View { - if let label { - PostHogLabelViewTagger(label: label) - } - } - } - - private struct PostHogLabelViewTagger: UIViewRepresentable { - let label: String - - func makeUIView(context _: Context) -> PostHogLabelTaggerView { - PostHogLabelTaggerView(label: label) - } - - func updateUIView(_: PostHogLabelTaggerView, context _: Context) { - // nothing - } - } - - private class PostHogLabelTaggerView: UIView { - private let label: String - weak var taggedView: UIView? - - init(label: String) { - self.label = label - super.init(frame: .zero) - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - label = "" - super.init(frame: .zero) - } - - override func layoutSubviews() { - super.didMoveToWindow() - - // try to find a "taggable" cousin view in hierarchy - // - // ### Why cousin view? - // - // Because of SwiftUI-to-UIKit view bridging: - // - // OriginalView (SwiftUI) - // L SwiftUITextFieldRepresentable (ViewRepresentable) - // L UITextField (UIControl) <- we tag here - // L PostHogLabelViewTagger (ViewRepresentable) - // L PostHogLabelTaggerView (UIView) <- we are here - // - if let view = findCousinView(of: PostHogSwiftUITaggable.self) { - taggedView = view - view.postHogLabel = label - } else { - // just tag grandparent view - // - // ### Why grandparent view? - // - // Because of SwiftUI-to-UIKit view bridging: - // OriginalView (SwiftUI) <- we tag here - // L PostHogLabelViewTagger (ViewRepresentable) - // L PostHogLabelTaggerView (UIView) <- we are here - // - taggedView = superview?.superview - superview?.superview?.postHogLabel = label - } - } - - override func removeFromSuperview() { - super.removeFromSuperview() - // remove custom label when removed from hierarchy - taggedView?.postHogLabel = nil - taggedView = nil - } - - private func findCousinView(of _: T.Type) -> T? { - for sibling in superview?.siblings() ?? [] { - if let match = sibling.child(of: T.self) { - return match - } - } - return nil - } - } - - // MARK: - Helpers - - private extension UIView { - func siblings() -> [UIView] { - superview?.subviews.reduce(into: []) { result, current in - if current !== self { result.append(current) } - } ?? [] - } - - func child(of type: T.Type) -> T? { - for child in subviews { - if let curT = child as? T ?? child.child(of: type) { - return curT - } - } - return nil - } - } - - protocol PostHogSwiftUITaggable: UIView { /**/ } - - extension UIControl: PostHogSwiftUITaggable { /**/ } - extension UIPickerView: PostHogSwiftUITaggable { /**/ } - extension UITextView: PostHogSwiftUITaggable { /**/ } - extension UICollectionView: PostHogSwiftUITaggable { /**/ } - extension UITableView: PostHogSwiftUITaggable { /**/ } - -#endif diff --git a/Pods/PostHog/PostHog/Autocapture/UIView+PostHogLabel.swift b/Pods/PostHog/PostHog/Autocapture/UIView+PostHogLabel.swift deleted file mode 100644 index 3f47d56..0000000 --- a/Pods/PostHog/PostHog/Autocapture/UIView+PostHogLabel.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// UIView+PostHogLabel.swift -// PostHog -// -// Created by Yiannis Josephides on 04/12/2024. -// - -#if os(iOS) || targetEnvironment(macCatalyst) - import UIKit - - public extension UIView { - /** - Adds a custom label to this view for use with PostHog's auto-capture functionality. - - By setting a custom label, you can easily identify and filter interactions with this specific element in your analytics data. - - ### Usage - ```swift - let myView = UIView() - myView.postHogLabel = "customLabel" - ``` - */ - var postHogLabel: String? { - get { objc_getAssociatedObject(self, &AssociatedKeys.phLabel) as? String } - set { objc_setAssociatedObject(self, &AssociatedKeys.phLabel, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - } - -#endif diff --git a/Pods/PostHog/PostHog/DI.swift b/Pods/PostHog/PostHog/DI.swift deleted file mode 100644 index 03c4a05..0000000 --- a/Pods/PostHog/PostHog/DI.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// DI.swift -// PostHog -// -// Created by Yiannis Josephides on 17/12/2024. -// - -// swiftlint:disable:next type_name -enum DI { - static var main = Container() - - final class Container { - // publishes global app lifecycle events - lazy var appLifecyclePublisher: AppLifecyclePublishing = ApplicationLifecyclePublisher.shared - // publishes global screen view events (UIViewController.viewDidAppear) - lazy var screenViewPublisher: ScreenViewPublishing = ApplicationScreenViewPublisher.shared - - #if os(iOS) || os(tvOS) - // publishes global application events (UIApplication.sendEvent) - lazy var applicationEventPublisher: ApplicationEventPublishing = ApplicationEventPublisher.shared - #endif - - #if os(iOS) - // publishes global view layout events within a throttle interval (UIView.layoutSubviews) - lazy var viewLayoutPublisher: ViewLayoutPublishing = ApplicationViewLayoutPublisher.shared - #endif - } -} diff --git a/Pods/PostHog/PostHog/Models/PostHogEvent.swift b/Pods/PostHog/PostHog/Models/PostHogEvent.swift deleted file mode 100644 index 178f0f2..0000000 --- a/Pods/PostHog/PostHog/Models/PostHogEvent.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// PostHogEvent.swift -// PostHog -// -// Created by Manoel Aranda Neto on 13.10.23. -// - -import Foundation - -@objc(PostHogEvent) public class PostHogEvent: NSObject { - @objc public var event: String - @objc public var distinctId: String - @objc public var properties: [String: Any] - @objc public var timestamp: Date - @objc public private(set) var uuid: UUID - // Only used for Replay - var apiKey: String? - - init(event: String, distinctId: String, properties: [String: Any]? = nil, timestamp: Date = Date(), uuid: UUID = UUID.v7(), apiKey: String? = nil) { - self.event = event - self.distinctId = distinctId - self.properties = properties ?? [:] - self.timestamp = timestamp - self.uuid = uuid - self.apiKey = apiKey - } - - // NOTE: Ideally we would use the NSCoding behaviour but it gets needlessly complex - // given we only need this for sending to the API - static func fromJSON(_ data: Data) -> PostHogEvent? { - guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { - return nil - } - - return fromJSON(json) - } - - static func fromJSON(_ json: [String: Any]) -> PostHogEvent? { - guard let event = json["event"] as? String else { return nil } - - let timestamp = json["timestamp"] as? String ?? toISO8601String(Date()) - - let timestampDate = toISO8601Date(timestamp) ?? Date() - - var properties = (json["properties"] as? [String: Any]) ?? [:] - - // back compatibility with v2 - let setProps = json["$set"] as? [String: Any] - if setProps != nil { - properties["$set"] = setProps - } - - guard let distinctId = (json["distinct_id"] as? String) ?? (properties["distinct_id"] as? String) else { return nil } - - let uuid = ((json["uuid"] as? String) ?? (json["message_id"] as? String)) ?? UUID.v7().uuidString - let uuidObj = UUID(uuidString: uuid) ?? UUID.v7() - - let apiKey = json["api_key"] as? String - - return PostHogEvent( - event: event, - distinctId: distinctId, - properties: properties, - timestamp: timestampDate, - uuid: uuidObj, - apiKey: apiKey - ) - } - - func toJSON() -> [String: Any] { - var json: [String: Any] = [ - "event": event, - "distinct_id": distinctId, - "properties": properties, - "timestamp": toISO8601String(timestamp), - "uuid": uuid.uuidString, - ] - - if let apiKey { - json["api_key"] = apiKey - } - - return json - } -} - -enum PostHogKnownUnsafeEditableEvent: String { - case snapshot = "$snapshot" - case screen = "$screen" - case set = "$set" - case surveyDismissed = "survey dismissed" - case surveySent = "survey sent" - case surveyShown = "survey shown" - case identify = "$identify" - case groupidentify = "$groupidentify" - case createAlias = "$create_alias" - case featureFlagCalled = "$feature_flag_called" - - static func contains(_ name: String) -> Bool { - PostHogKnownUnsafeEditableEvent(rawValue: name) != nil - } -} diff --git a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurvey+Display.swift b/Pods/PostHog/PostHog/Models/Surveys/PostHogSurvey+Display.swift deleted file mode 100644 index 784bda1..0000000 --- a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurvey+Display.swift +++ /dev/null @@ -1,122 +0,0 @@ -#if os(iOS) || TESTING - import Foundation - - extension PostHogSurvey { - func toDisplaySurvey() -> PostHogDisplaySurvey { - PostHogDisplaySurvey( - id: id, - name: name, - questions: questions.compactMap { $0.toDisplayQuestion() }, - appearance: appearance?.toDisplayAppearance(), - startDate: startDate, - endDate: endDate - ) - } - } - - extension PostHogSurveyQuestion { - func toDisplayQuestion() -> PostHogDisplaySurveyQuestion? { - switch self { - case let .open(question): - return PostHogDisplayOpenQuestion( - id: question.id, - question: question.question, - questionDescription: question.description, - questionDescriptionContentType: question.descriptionContentType?.toDisplayContentType(), - isOptional: question.optional ?? false, - buttonText: question.buttonText - ) - - case let .link(question): - return PostHogDisplayLinkQuestion( - id: question.id, - question: question.question, - questionDescription: question.description, - questionDescriptionContentType: question.descriptionContentType?.toDisplayContentType(), - isOptional: question.optional ?? false, - buttonText: question.buttonText, - link: question.link ?? "" - ) - - case let .rating(question): - return PostHogDisplayRatingQuestion( - id: question.id, - question: question.question, - questionDescription: question.description, - questionDescriptionContentType: question.descriptionContentType?.toDisplayContentType(), - isOptional: question.optional ?? false, - buttonText: question.buttonText, - ratingType: question.display.toDisplayRatingType(), - scaleLowerBound: question.scale.range.lowerBound, - scaleUpperBound: question.scale.range.upperBound, - lowerBoundLabel: question.lowerBoundLabel, - upperBoundLabel: question.upperBoundLabel - ) - - case let .singleChoice(question), let .multipleChoice(question): - return PostHogDisplayChoiceQuestion( - id: question.id, - question: question.question, - questionDescription: question.description, - questionDescriptionContentType: question.descriptionContentType?.toDisplayContentType(), - isOptional: question.optional ?? false, - buttonText: question.buttonText, - choices: question.choices, - hasOpenChoice: question.hasOpenChoice ?? false, - shuffleOptions: question.shuffleOptions ?? false, - isMultipleChoice: isMultipleChoice - ) - - default: - return nil - } - } - - private var isMultipleChoice: Bool { - switch self { - case .multipleChoice: return true - default: return false - } - } - } - - extension PostHogSurveyTextContentType { - func toDisplayContentType() -> PostHogDisplaySurveyTextContentType { - if case .html = self { - return .html - } - return .text - } - } - - extension PostHogSurveyRatingDisplayType { - func toDisplayRatingType() -> PostHogDisplaySurveyRatingType { - if case .emoji = self { - return .emoji - } - return .number - } - } - - extension PostHogSurveyAppearance { - func toDisplayAppearance() -> PostHogDisplaySurveyAppearance { - PostHogDisplaySurveyAppearance( - fontFamily: fontFamily, - backgroundColor: backgroundColor, - borderColor: borderColor, - submitButtonColor: submitButtonColor, - submitButtonText: submitButtonText, - submitButtonTextColor: submitButtonTextColor, - descriptionTextColor: descriptionTextColor, - ratingButtonColor: ratingButtonColor, - ratingButtonActiveColor: ratingButtonActiveColor, - placeholder: placeholder, - displayThankYouMessage: displayThankYouMessage ?? true, - thankYouMessageHeader: thankYouMessageHeader, - thankYouMessageDescription: thankYouMessageDescription, - thankYouMessageDescriptionContentType: thankYouMessageDescriptionContentType?.toDisplayContentType(), - thankYouMessageCloseButtonText: thankYouMessageCloseButtonText - ) - } - } -#endif diff --git a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurvey.swift b/Pods/PostHog/PostHog/Models/Surveys/PostHogSurvey.swift deleted file mode 100644 index 8b045fe..0000000 --- a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurvey.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// PostHogSurvey.swift -// PostHog -// -// Created by Yiannis Josephides on 20/01/2025. -// - -import Foundation - -/// Represents the main survey object containing metadata, questions, conditions, and appearance settings. -/// see: posthog-js/posthog-surveys-types.ts -struct PostHogSurvey: Decodable, Identifiable { - /// The unique identifier for the survey - let id: String - /// The name of the survey - let name: String - /// Type of the survey (e.g., "popover") - let type: PostHogSurveyType - /// The questions asked in the survey - let questions: [PostHogSurveyQuestion] - /// Multiple feature flag keys. Must all (AND) evaluate to true for the survey to be shown (optional) - let featureFlagKeys: [PostHogSurveyFeatureFlagKeyValue]? - /// Linked feature flag key. Must evaluate to true for the survey to be shown (optional) - let linkedFlagKey: String? - /// Targeting feature flag key. Must evaluate to true for the survey to be shown (optional) - let targetingFlagKey: String? - /// Internal targeting flag key. Must evaluate to true for the survey to be shown (optional) - let internalTargetingFlagKey: String? - /// Conditions for displaying the survey (optional) - let conditions: PostHogSurveyConditions? - /// Appearance settings for the survey (optional) - let appearance: PostHogSurveyAppearance? - /// The iteration number for the survey (optional) - let currentIteration: Int? - /// The start date for the current iteration of the survey (optional) - let currentIterationStartDate: Date? - /// Start date of the survey (optional) - let startDate: Date? - /// End date of the survey (optional) - let endDate: Date? -} - -struct PostHogSurveyFeatureFlagKeyValue: Equatable, Decodable { - let key: String - let value: String? -} diff --git a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyAppearance.swift b/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyAppearance.swift deleted file mode 100644 index 95d213c..0000000 --- a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyAppearance.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// PostHogSurveyAppearance.swift -// PostHog -// -// Created by Ioannis Josephides on 08/04/2025. -// - -import Foundation - -/// Represents the appearance settings for the survey, such as colors, fonts, and layout -struct PostHogSurveyAppearance: Decodable { - let position: PostHogSurveyAppearancePosition? - let fontFamily: String? - let backgroundColor: String? - let submitButtonColor: String? - let submitButtonText: String? - let submitButtonTextColor: String? - let descriptionTextColor: String? - let ratingButtonColor: String? - let ratingButtonActiveColor: String? - let ratingButtonHoverColor: String? - let whiteLabel: Bool? - let autoDisappear: Bool? - let displayThankYouMessage: Bool? - let thankYouMessageHeader: String? - let thankYouMessageDescription: String? - let thankYouMessageDescriptionContentType: PostHogSurveyTextContentType? - let thankYouMessageCloseButtonText: String? - let borderColor: String? - let placeholder: String? - let shuffleQuestions: Bool? - let surveyPopupDelaySeconds: TimeInterval? - // widget options - let widgetType: PostHogSurveyAppearanceWidgetType? - let widgetSelector: String? - let widgetLabel: String? - let widgetColor: String? -} diff --git a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyConditions.swift b/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyConditions.swift deleted file mode 100644 index 0cf3cb1..0000000 --- a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyConditions.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// PostHogSurveyConditions.swift -// PostHog -// -// Created by Ioannis Josephides on 08/04/2025. -// - -import Foundation - -/// Represents conditions for displaying the survey, such as URL or event-based triggers -struct PostHogSurveyConditions: Decodable { - /// Target URL for the survey (optional) - let url: String? - /// The match type for the url condition (optional) - let urlMatchType: PostHogSurveyMatchType? - /// CSS selector for displaying the survey (optional) - let selector: String? - /// Device type based conditions for displaying the survey (optional) - let deviceTypes: [String]? - /// The match type for the device type condition (optional) - let deviceTypesMatchType: PostHogSurveyMatchType? - /// Minimum wait period before showing the survey again (optional) - let seenSurveyWaitPeriodInDays: Int? - /// Event-based conditions for displaying the survey (optional) - let events: PostHogSurveyEventConditions? - /// Action-based conditions for displaying the survey (optional) - let actions: PostHogSurveyActionsConditions? -} - -/// Represents event-based conditions for displaying the survey -struct PostHogSurveyEventConditions: Decodable { - let repeatedActivation: Bool? - /// List of events that trigger the survey - let values: [PostHogEventCondition] -} - -/// Represents action-based conditions for displaying the survey -struct PostHogSurveyActionsConditions: Decodable { - /// List of events that trigger the survey - let values: [PostHogEventCondition] -} - -/// Represents a single event condition used in survey targeting -struct PostHogEventCondition: Decodable, Equatable { - /// Name of the event (e.g., "content loaded") - let name: String -} diff --git a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyEnums.swift b/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyEnums.swift deleted file mode 100644 index 6615401..0000000 --- a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyEnums.swift +++ /dev/null @@ -1,280 +0,0 @@ -// -// PostHogSurveyEnums.swift -// PostHog -// -// Created by Ioannis Josephides on 08/04/2025. -// - -import Foundation - -// MARK: - Supporting Types - -enum PostHogSurveyType: Decodable, Equatable { - case popover - case api - case widget - case unknown(type: String) - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let typeString = try container.decode(String.self) - - switch typeString { - case "popover": - self = .popover - case "api": - self = .api - case "widget": - self = .widget - default: - self = .unknown(type: typeString) - } - } -} - -enum PostHogSurveyQuestionType: Decodable, Equatable { - case open - case link - case rating - case multipleChoice - case singleChoice - case unknown(type: String) - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let typeString = try container.decode(String.self) - - switch typeString { - case "open": - self = .open - case "link": - self = .link - case "rating": - self = .rating - case "multiple_choice": - self = .multipleChoice - case "single_choice": - self = .singleChoice - default: - self = .unknown(type: typeString) - } - } -} - -enum PostHogSurveyTextContentType: Decodable, Equatable { - case html - case text - case unknown(type: String) - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let typeString = try container.decode(String.self) - - switch typeString { - case "html": - self = .html - case "text": - self = .text - default: - self = .unknown(type: typeString) - } - } -} - -enum PostHogSurveyMatchType: Decodable, Equatable { - case regex - case notRegex - case exact - case isNot - case iContains - case notIContains - case unknown(value: String) - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let valueString = try container.decode(String.self) - - switch valueString { - case "regex": - self = .regex - case "not_regex": - self = .notRegex - case "exact": - self = .exact - case "is_not": - self = .isNot - case "icontains": - self = .iContains - case "not_icontains": - self = .notIContains - default: - self = .unknown(value: valueString) - } - } -} - -enum PostHogSurveyAppearancePosition: Decodable, Equatable { - case topLeft - case topCenter - case topRight - case middleLeft - case middleCenter - case middleRight - case left - case right - case center - case unknown(position: String) - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let positionString = try container.decode(String.self) - - switch positionString { - case "top_left": - self = .topLeft - case "top_center": - self = .topCenter - case "top_right": - self = .topRight - case "middle_left": - self = .middleLeft - case "middle_center": - self = .middleCenter - case "middle_right": - self = .middleRight - case "left": - self = .left - case "right": - self = .right - case "center": - self = .center - default: - self = .unknown(position: positionString) - } - } -} - -enum PostHogSurveyAppearanceWidgetType: Decodable, Equatable { - case button - case tab - case selector - case unknown(type: String) - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let typeString = try container.decode(String.self) - - switch typeString { - case "button": - self = .button - case "tab": - self = .tab - case "selector": - self = .selector - default: - self = .unknown(type: typeString) - } - } -} - -enum PostHogSurveyRatingDisplayType: Decodable, Equatable { - case number - case emoji - case unknown(type: String) - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let typeString = try container.decode(String.self) - - switch typeString { - case "number": - self = .number - case "emoji": - self = .emoji - default: - self = .unknown(type: typeString) - } - } -} - -enum PostHogSurveyRatingScale: Decodable, Equatable { - case threePoint - case fivePoint - case sevenPoint - case tenPoint - case unknown(scale: Int) - - var rawValue: Int { - switch self { - case .threePoint: 3 - case .fivePoint: 5 - case .sevenPoint: 7 - case .tenPoint: 10 - case let .unknown(scale): scale - } - } - - var range: ClosedRange { - switch self { - case .threePoint: 1 ... 3 - case .fivePoint: 1 ... 5 - case .sevenPoint: 1 ... 7 - case .tenPoint: 0 ... 10 - case let .unknown(scale): 1 ... scale - } - } - - init(range: ClosedRange) { - switch range { - case 1 ... 3: self = .threePoint - case 1 ... 5: self = .fivePoint - case 1 ... 7: self = .sevenPoint - case 0 ... 10: self = .tenPoint - default: self = .unknown(scale: range.upperBound) - } - } - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let scaleInt = try container.decode(Int.self) - - switch scaleInt { - case 3: - self = .threePoint - case 5: - self = .fivePoint - case 7: - self = .sevenPoint - case 10: - self = .tenPoint - default: - self = .unknown(scale: scaleInt) - } - } -} - -enum PostHogSurveyQuestionBranchingType: Decodable, Equatable { - case nextQuestion - case end - case responseBased - case specificQuestion - case unknown(type: String) - - init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let typeString = try container.decode(String.self) - - switch typeString { - case "next_question": - self = .nextQuestion - case "end": - self = .end - case "response_based": - self = .responseBased - case "specific_question": - self = .specificQuestion - default: - self = .unknown(type: typeString) - } - } -} diff --git a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyQuestion.swift b/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyQuestion.swift deleted file mode 100644 index d974b56..0000000 --- a/Pods/PostHog/PostHog/Models/Surveys/PostHogSurveyQuestion.swift +++ /dev/null @@ -1,247 +0,0 @@ -// -// PostHogSurveyQuestion.swift -// PostHog -// -// Created by Ioannis Josephides on 08/04/2025. -// - -import Foundation - -// MARK: - Question Models - -/// Protocol defining common properties for all survey question types -protocol PostHogSurveyQuestionProperties { - /// Question ID, empty if none - var id: String { get } - /// Question text - var question: String { get } - /// Additional description or instructions (optional) - var description: String? { get } - /// Content type of the description (e.g., "text", "html") (optional) - var descriptionContentType: PostHogSurveyTextContentType? { get } - /// Indicates if this question is optional (optional) - var optional: Bool? { get } - /// Text for the main CTA associated with this question (optional) - var buttonText: String? { get } - /// Original index of the question in the survey (optional) - var originalQuestionIndex: Int? { get } - /// Question branching logic if any (optional) - var branching: PostHogSurveyQuestionBranching? { get } -} - -/// Represents different types of survey questions with their associated data -enum PostHogSurveyQuestion: PostHogSurveyQuestionProperties, Decodable { - case open(PostHogOpenSurveyQuestion) - case link(PostHogLinkSurveyQuestion) - case rating(PostHogRatingSurveyQuestion) - case singleChoice(PostHogMultipleSurveyQuestion) - case multipleChoice(PostHogMultipleSurveyQuestion) - case unknown(type: String) - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let type = try container.decode(PostHogSurveyQuestionType.self, forKey: .type) - - switch type { - case .open: - self = try .open(PostHogOpenSurveyQuestion(from: decoder)) - case .link: - self = try .link(PostHogLinkSurveyQuestion(from: decoder)) - case .rating: - self = try .rating(PostHogRatingSurveyQuestion(from: decoder)) - case .singleChoice: - self = try .singleChoice(PostHogMultipleSurveyQuestion(from: decoder)) - case .multipleChoice: - self = try .multipleChoice(PostHogMultipleSurveyQuestion(from: decoder)) - case let .unknown(type): - self = .unknown(type: type) - } - } - - var id: String { - wrappedQuestion?.id ?? "" - } - - var question: String { - wrappedQuestion?.question ?? "" - } - - var description: String? { - wrappedQuestion?.description - } - - var descriptionContentType: PostHogSurveyTextContentType? { - wrappedQuestion?.descriptionContentType - } - - var optional: Bool? { - wrappedQuestion?.optional - } - - var buttonText: String? { - wrappedQuestion?.buttonText - } - - var originalQuestionIndex: Int? { - wrappedQuestion?.originalQuestionIndex - } - - var branching: PostHogSurveyQuestionBranching? { - wrappedQuestion?.branching - } - - private var wrappedQuestion: PostHogSurveyQuestionProperties? { - switch self { - case let .open(question): question - case let .link(question): question - case let .rating(question): question - case let .singleChoice(question): question - case let .multipleChoice(question): question - case .unknown: nil - } - } - - private enum CodingKeys: CodingKey { - case type - } -} - -/// Represents a basic open-ended survey question -struct PostHogOpenSurveyQuestion: PostHogSurveyQuestionProperties, Decodable { - let id: String - let question: String - let description: String? - let descriptionContentType: PostHogSurveyTextContentType? - let optional: Bool? - let buttonText: String? - let originalQuestionIndex: Int? - let branching: PostHogSurveyQuestionBranching? -} - -/// Represents a survey question with an associated link -struct PostHogLinkSurveyQuestion: PostHogSurveyQuestionProperties, Decodable { - let id: String - let question: String - let description: String? - let descriptionContentType: PostHogSurveyTextContentType? - let optional: Bool? - let buttonText: String? - let originalQuestionIndex: Int? - let branching: PostHogSurveyQuestionBranching? - /// URL link associated with the question - let link: String? -} - -/// Represents a rating-based survey question -struct PostHogRatingSurveyQuestion: PostHogSurveyQuestionProperties, Decodable { - let id: String - let question: String - let description: String? - let descriptionContentType: PostHogSurveyTextContentType? - let optional: Bool? - let buttonText: String? - let originalQuestionIndex: Int? - let branching: PostHogSurveyQuestionBranching? - /// Display type for the rating ("number" or "emoji") - let display: PostHogSurveyRatingDisplayType - /// Scale of the rating (3, 5, 7, or 10) - let scale: PostHogSurveyRatingScale - let lowerBoundLabel: String - let upperBoundLabel: String -} - -/// Represents a multiple-choice or single-choice survey question -struct PostHogMultipleSurveyQuestion: PostHogSurveyQuestionProperties, Decodable { - let id: String - let question: String - let description: String? - let descriptionContentType: PostHogSurveyTextContentType? - let optional: Bool? - let buttonText: String? - let originalQuestionIndex: Int? - let branching: PostHogSurveyQuestionBranching? - /// List of choices for multiple-choice or single-choice questions - let choices: [String] - /// Indicates if there is an open choice option (optional) - let hasOpenChoice: Bool? - /// Indicates if choices should be shuffled or not (optional) - let shuffleOptions: Bool? -} - -/// Represents branching logic for a question based on user responses -enum PostHogSurveyQuestionBranching: Decodable { - case next - case end - case responseBased(responseValues: [String: Any]) - case specificQuestion(index: Int) - case unknown(type: String) - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let type = try container.decode(PostHogSurveyQuestionBranchingType.self, forKey: .type) - - switch type { - case .nextQuestion: - self = .next - case .end: - self = .end - case .responseBased: - do { - let responseValues = try container.decode(JSON.self, forKey: .responseValues) - guard let dict = responseValues.value as? [String: Any] else { - throw DecodingError.typeMismatch( - [String: Any].self, - DecodingError.Context( - codingPath: container.codingPath, - debugDescription: "Expected responseValues to be a dictionary" - ) - ) - } - self = .responseBased(responseValues: dict) - } catch { - throw DecodingError.dataCorruptedError( - forKey: .responseValues, - in: container, - debugDescription: "responseValues is not a valid JSON object" - ) - } - case .specificQuestion: - self = try .specificQuestion(index: container.decode(Int.self, forKey: .index)) - case let .unknown(type): - self = .unknown(type: type) - } - } - - private enum CodingKeys: CodingKey { - case type, responseValues, index - } -} - -/// A helper type for decoding JSON values, which may be nested objects, arrays, strings, numbers, booleans, or nulls. -private struct JSON: Decodable { - let value: Any - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - - if container.decodeNil() { - value = NSNull() - } else if let object = try? container.decode([String: JSON].self) { - value = object.mapValues { $0.value } - } else if let array = try? container.decode([JSON].self) { - value = array.map(\.value) - } else if let string = try? container.decode(String.self) { - value = string - } else if let bool = try? container.decode(Bool.self) { - value = bool - } else if let number = try? container.decode(Double.self) { - value = NSNumber(value: number) - } else if let number = try? container.decode(Int.self) { - value = NSNumber(value: number) - } else { - throw DecodingError.dataCorruptedError( - in: container, debugDescription: "Invalid JSON value" - ) - } - } -} diff --git a/Pods/PostHog/PostHog/PostHog.h b/Pods/PostHog/PostHog/PostHog.h deleted file mode 100644 index 79e356a..0000000 --- a/Pods/PostHog/PostHog/PostHog.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// PostHog.h -// PostHog -// -// Created by Ben White on 10.01.23. -// - -#import - -//! Project version number for PostHog. -FOUNDATION_EXPORT double PostHogVersionNumber; - -//! Project version string for PostHog. -FOUNDATION_EXPORT const unsigned char PostHogVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import diff --git a/Pods/PostHog/PostHog/PostHogApi.swift b/Pods/PostHog/PostHog/PostHogApi.swift deleted file mode 100644 index 53bee88..0000000 --- a/Pods/PostHog/PostHog/PostHogApi.swift +++ /dev/null @@ -1,337 +0,0 @@ -// -// PostHogApi.swift -// PostHog -// -// Created by Ben White on 06.02.23. -// - -import Foundation - -class PostHogApi { - private let config: PostHogConfig - - // default is 60s but we do 10s - private let defaultTimeout: TimeInterval = 10 - - init(_ config: PostHogConfig) { - self.config = config - } - - func sessionConfig() -> URLSessionConfiguration { - let config = URLSessionConfiguration.default - - config.httpAdditionalHeaders = [ - "Content-Type": "application/json; charset=utf-8", - "User-Agent": "\(postHogSdkName)/\(postHogVersion)", - ] - - return config - } - - private func getURLRequest(_ url: URL) -> URLRequest { - var request = URLRequest(url: url) - request.httpMethod = "POST" - request.timeoutInterval = defaultTimeout - return request - } - - private func getEndpointURL( - _ endpoint: String, - queryItems: URLQueryItem..., - relativeTo baseUrl: URL - ) -> URL? { - guard var components = URLComponents( - url: baseUrl, - resolvingAgainstBaseURL: true - ) else { - return nil - } - let path = "\(components.path)/\(endpoint)" - .replacingOccurrences(of: "/+", with: "/", options: .regularExpression) - components.path = path - components.queryItems = queryItems - return components.url - } - - private func getRemoteConfigRequest() -> URLRequest? { - guard let baseUrl: URL = switch config.host.absoluteString { - case "https://us.i.posthog.com": - URL(string: "https://us-assets.i.posthog.com") - case "https://eu.i.posthog.com": - URL(string: "https://eu-assets.i.posthog.com") - default: - config.host - } else { - return nil - } - - let url = baseUrl.appendingPathComponent("/array/\(config.apiKey)/config") - - var request = URLRequest(url: url) - request.httpMethod = "GET" - request.timeoutInterval = defaultTimeout - return request - } - - func batch(events: [PostHogEvent], completion: @escaping (PostHogBatchUploadInfo) -> Void) { - guard let url = getEndpointURL("/batch", relativeTo: config.host) else { - hedgeLog("Malformed batch URL error.") - return completion(PostHogBatchUploadInfo(statusCode: nil, error: nil)) - } - - let config = sessionConfig() - var headers = config.httpAdditionalHeaders ?? [:] - headers["Accept-Encoding"] = "gzip" - headers["Content-Encoding"] = "gzip" - config.httpAdditionalHeaders = headers - - let request = getURLRequest(url) - - let toSend: [String: Any] = [ - "api_key": self.config.apiKey, - "batch": events.map { $0.toJSON() }, - "sent_at": toISO8601String(Date()), - ] - - var data: Data? - - do { - data = try JSONSerialization.data(withJSONObject: toSend) - } catch { - hedgeLog("Error parsing the batch body: \(error)") - return completion(PostHogBatchUploadInfo(statusCode: nil, error: error)) - } - - var gzippedPayload: Data? - do { - gzippedPayload = try data!.gzipped() - } catch { - hedgeLog("Error gzipping the batch body: \(error).") - return completion(PostHogBatchUploadInfo(statusCode: nil, error: error)) - } - - URLSession(configuration: config).uploadTask(with: request, from: gzippedPayload!) { data, response, error in - if error != nil { - hedgeLog("Error calling the batch API: \(String(describing: error)).") - return completion(PostHogBatchUploadInfo(statusCode: nil, error: error)) - } - - let httpResponse = response as! HTTPURLResponse - - if !(200 ... 299 ~= httpResponse.statusCode) { - let jsonBody = String(describing: try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]) - let errorMessage = "Error sending events to batch API: status: \(jsonBody)." - hedgeLog(errorMessage) - } else { - hedgeLog("Events sent successfully.") - } - - return completion(PostHogBatchUploadInfo(statusCode: httpResponse.statusCode, error: error)) - }.resume() - } - - func snapshot(events: [PostHogEvent], completion: @escaping (PostHogBatchUploadInfo) -> Void) { - guard let url = getEndpointURL(config.snapshotEndpoint, relativeTo: config.host) else { - hedgeLog("Malformed snapshot URL error.") - return completion(PostHogBatchUploadInfo(statusCode: nil, error: nil)) - } - - for event in events { - event.apiKey = self.config.apiKey - } - - let config = sessionConfig() - var headers = config.httpAdditionalHeaders ?? [:] - headers["Accept-Encoding"] = "gzip" - headers["Content-Encoding"] = "gzip" - config.httpAdditionalHeaders = headers - - let request = getURLRequest(url) - - let toSend = events.map { $0.toJSON() } - - var data: Data? - - do { - data = try JSONSerialization.data(withJSONObject: toSend) -// remove it only for debugging -// if let newData = data { -// let convertedString = String(data: newData, encoding: .utf8) -// hedgeLog("snapshot body: \(convertedString ?? "")") -// } - } catch { - hedgeLog("Error parsing the snapshot body: \(error)") - return completion(PostHogBatchUploadInfo(statusCode: nil, error: error)) - } - - var gzippedPayload: Data? - do { - gzippedPayload = try data!.gzipped() - } catch { - hedgeLog("Error gzipping the snapshot body: \(error).") - return completion(PostHogBatchUploadInfo(statusCode: nil, error: error)) - } - - URLSession(configuration: config).uploadTask(with: request, from: gzippedPayload!) { data, response, error in - if error != nil { - hedgeLog("Error calling the snapshot API: \(String(describing: error)).") - return completion(PostHogBatchUploadInfo(statusCode: nil, error: error)) - } - - let httpResponse = response as! HTTPURLResponse - - if !(200 ... 299 ~= httpResponse.statusCode) { - let jsonBody = String(describing: try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]) - let errorMessage = "Error sending events to snapshot API: status: \(httpResponse.statusCode), body: \(jsonBody)." - hedgeLog(errorMessage) - } else { - hedgeLog("Snapshots sent successfully.") - } - - return completion(PostHogBatchUploadInfo(statusCode: httpResponse.statusCode, error: error)) - }.resume() - } - - func flags( - distinctId: String, - anonymousId: String?, - groups: [String: String], - personProperties: [String: Any], - groupProperties: [String: [String: Any]]? = nil, - completion: @escaping ([String: Any]?, _ error: Error?) -> Void - ) { - let url = getEndpointURL( - "/flags", - queryItems: URLQueryItem(name: "v", value: "2"), URLQueryItem(name: "config", value: "true"), - relativeTo: config.host - ) - - guard let url else { - hedgeLog("Malformed flags URL error.") - return completion(nil, nil) - } - - let config = sessionConfig() - - let request = getURLRequest(url) - - var toSend: [String: Any] = [ - "api_key": self.config.apiKey, - "distinct_id": distinctId, - "$groups": groups, - ] - - if let anonymousId { - toSend["$anon_distinct_id"] = anonymousId - } - - if !personProperties.isEmpty { - toSend["person_properties"] = personProperties - } - - if let groupProperties, !groupProperties.isEmpty { - toSend["group_properties"] = groupProperties - } - - if let evaluationEnvironments = self.config.evaluationEnvironments, !evaluationEnvironments.isEmpty { - toSend["evaluation_environments"] = evaluationEnvironments - } - - var data: Data? - - do { - data = try JSONSerialization.data(withJSONObject: toSend) - } catch { - hedgeLog("Error parsing the flags body: \(error)") - return completion(nil, error) - } - - URLSession(configuration: config).uploadTask(with: request, from: data!) { data, response, error in - if error != nil { - hedgeLog("Error calling the flags API: \(String(describing: error))") - return completion(nil, error) - } - - let httpResponse = response as! HTTPURLResponse - - if !(200 ... 299 ~= httpResponse.statusCode) { - let jsonBody = String(describing: try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]) - let errorMessage = "Error calling flags API: status: \(httpResponse.statusCode), body: \(jsonBody)." - hedgeLog(errorMessage) - - return completion(nil, - InternalPostHogError(description: errorMessage)) - } else { - hedgeLog("Flags called successfully.") - } - - do { - let jsonData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] - completion(jsonData, nil) - } catch { - hedgeLog("Error parsing the flags response: \(error)") - completion(nil, error) - } - }.resume() - } - - func remoteConfig( - completion: @escaping ([String: Any]?, _ error: Error?) -> Void - ) { - guard let request = getRemoteConfigRequest() else { - hedgeLog("Error calling the remote config API: unable to create request") - return - } - - let config = sessionConfig() - - let task = URLSession(configuration: config).dataTask(with: request) { data, response, error in - if let error { - hedgeLog("Error calling the remote config API: \(error.localizedDescription)") - return completion(nil, error) - } - - let httpResponse = response as! HTTPURLResponse - - if !(200 ... 299 ~= httpResponse.statusCode) { - let jsonBody = String(describing: try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]) - let errorMessage = "Error calling the remote config API: status: \(httpResponse.statusCode), body: \(jsonBody)." - hedgeLog(errorMessage) - - return completion(nil, - InternalPostHogError(description: errorMessage)) - } else { - hedgeLog("Remote config called successfully.") - } - - do { - let jsonData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] - completion(jsonData, nil) - } catch { - hedgeLog("Error parsing the remote config response: \(error)") - completion(nil, error) - } - } - - task.resume() - } -} - -extension PostHogApi { - static var jsonDecoder: JSONDecoder = { - let decoder = JSONDecoder() - - decoder.dateDecodingStrategy = .custom { decoder in - let container = try decoder.singleValueContainer() - let dateString = try container.decode(String.self) - guard let date = apiDateFormatter.date(from: dateString) else { - throw DecodingError.dataCorruptedError( - in: container, debugDescription: "Invalid date format" - ) - } - return date - } - decoder.keyDecodingStrategy = .convertFromSnakeCase - return decoder - }() -} diff --git a/Pods/PostHog/PostHog/PostHogBatchUploadInfo.swift b/Pods/PostHog/PostHog/PostHogBatchUploadInfo.swift deleted file mode 100644 index 9b4d607..0000000 --- a/Pods/PostHog/PostHog/PostHogBatchUploadInfo.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// PostHogBatchUploadInfo.swift -// PostHog -// -// Created by Manoel Aranda Neto on 13.10.23. -// - -import Foundation - -struct PostHogBatchUploadInfo { - let statusCode: Int? - let error: Error? -} diff --git a/Pods/PostHog/PostHog/PostHogConfig.swift b/Pods/PostHog/PostHog/PostHogConfig.swift deleted file mode 100644 index 2542efd..0000000 --- a/Pods/PostHog/PostHog/PostHogConfig.swift +++ /dev/null @@ -1,276 +0,0 @@ -// -// PostHogConfig.swift -// PostHog -// -// Created by Ben White on 07.02.23. -// -import Foundation - -public typealias BeforeSendBlock = (PostHogEvent) -> PostHogEvent? - -@objc public final class BoxedBeforeSendBlock: NSObject { - @objc public let block: BeforeSendBlock - - @objc(block:) - public init(block: @escaping BeforeSendBlock) { - self.block = block - } -} - -@objc(PostHogConfig) public class PostHogConfig: NSObject { - enum Defaults { - #if os(tvOS) - static let flushAt: Int = 5 - static let maxQueueSize: Int = 100 - #else - static let flushAt: Int = 20 - static let maxQueueSize: Int = 1000 - #endif - static let maxBatchSize: Int = 50 - static let flushIntervalSeconds: TimeInterval = 30 - } - - @objc(PostHogDataMode) public enum PostHogDataMode: Int { - case wifi - case cellular - case any - } - - @objc public let host: URL - @objc public let apiKey: String - @objc public var flushAt: Int = Defaults.flushAt - @objc public var maxQueueSize: Int = Defaults.maxQueueSize - @objc public var maxBatchSize: Int = Defaults.maxBatchSize - @objc public var flushIntervalSeconds: TimeInterval = Defaults.flushIntervalSeconds - @objc public var dataMode: PostHogDataMode = .any - @objc public var sendFeatureFlagEvent: Bool = true - @objc public var preloadFeatureFlags: Bool = true - - /// Preload PostHog remote config automatically - /// Default: true - /// - /// Note: Surveys rely on remote config. Disabling this will also disable Surveys - @objc public var remoteConfig: Bool = true - - @objc public var captureApplicationLifecycleEvents: Bool = true - @objc public var captureScreenViews: Bool = true - - /// Enable method swizzling for SDK functionality that depends on it - /// - /// When disabled, functionality that require swizzling (like autocapture, screen views, session replay, surveys) will not be installed. - /// - /// Note: Disabling swizzling will limit session rotation logic to only detect application open and background events. - /// Session rotation will still work, just with reduced granularity for detecting user activity. - /// - /// Default: true - @objc public var enableSwizzling: Bool = true - - #if os(iOS) || targetEnvironment(macCatalyst) - /// Enable autocapture for iOS - /// Default: false - @objc public var captureElementInteractions: Bool = false - #endif - @objc public var debug: Bool = false - @objc public var optOut: Bool = false - @objc public var getAnonymousId: ((UUID) -> UUID) = { uuid in uuid } - - /// Flag to reuse the anonymous Id between `reset()` and next `identify()` calls - /// - /// If enabled, the anonymous Id will be reused for all anonymous users on this device, - /// essentially creating a "Guest user Id" as long as this option is enabled. - /// - /// Note: - /// Events captured *before* call to *identify()* won't be linked to the identified user - /// Events captured *after* call to *reset()* won't be linked to the identified user - /// - /// Defaults to false. - @objc public var reuseAnonymousId: Bool = false - - /// Hook that allows to sanitize the event properties - /// The hook is called before the event is cached or sent over the wire - @available(*, deprecated, message: "Use beforeSend instead") - @objc public var propertiesSanitizer: PostHogPropertiesSanitizer? - /// Determines the behavior for processing user profiles. - @objc public var personProfiles: PostHogPersonProfiles = .identifiedOnly - - /// Automatically set common device and app properties as person properties for feature flag evaluation. - /// - /// When enabled, the SDK will automatically set the following person properties: - /// - $app_version: App version from bundle - /// - $app_build: App build number from bundle - /// - $os_name: Operating system name (iOS, macOS, etc.) - /// - $os_version: Operating system version - /// - $device_type: Device type (Mobile, Tablet, Desktop, etc.) - /// - $locale: User's current locale - /// - /// This helps ensure feature flags that rely on these properties work correctly - /// without waiting for server-side processing of identify() calls. - /// - /// Default: true - @objc public var setDefaultPersonProperties: Bool = true - - /// Evaluation environments for feature flags. - /// - /// When configured, only feature flags that have at least one matching evaluation tag - /// will be evaluated. Feature flags with no evaluation tags will always be evaluated - /// for backward compatibility. - /// - /// Example usage: - /// ```swift - /// config.evaluationEnvironments = ["production", "web", "checkout"] - /// ``` - /// - /// This helps ensure feature flags are only evaluated in the appropriate environments - /// for your SDK instance. - /// - /// Default: nil (all flags are evaluated) - @objc public var evaluationEnvironments: [String]? - - /// The identifier of the App Group that should be used to store shared analytics data. - /// PostHog will try to get the physical location of the App Group’s shared container, otherwise fallback to the default location - /// Default: nil - @objc public var appGroupIdentifier: String? - - /// Internal - /// Do not modify it, this flag is read and updated by the SDK via feature flags - @objc public var snapshotEndpoint: String = "/s/" - - /// or EU Host: 'https://eu.i.posthog.com' - public static let defaultHost: String = "https://us.i.posthog.com" - - #if os(iOS) - /// Enable Recording of Session Replays for iOS - /// Default: false - @objc public var sessionReplay: Bool = false - /// Session Replay configuration - @objc public let sessionReplayConfig: PostHogSessionReplayConfig = .init() - #endif - - /// Enable mobile surveys - /// - /// Default: true - /// - /// Note: Event triggers will only work with the instance that first enables surveys. - /// In case of multiple instances, please make sure you are capturing events on the instance that has config.surveys = true - @available(iOS 15.0, *) - @available(watchOS, unavailable, message: "Surveys are only available on iOS 15+") - @available(macOS, unavailable, message: "Surveys are only available on iOS 15+") - @available(tvOS, unavailable, message: "Surveys are only available on iOS 15+") - @available(visionOS, unavailable, message: "Surveys are only available on iOS 15+") - @objc public var surveys: Bool { - get { _surveys } - set { setSurveys(newValue) } - } - - @available(iOS 15.0, *) - @available(watchOS, unavailable, message: "Surveys are only available on iOS 15+") - @available(macOS, unavailable, message: "Surveys are only available on iOS 15+") - @available(tvOS, unavailable, message: "Surveys are only available on iOS 15+") - @available(visionOS, unavailable, message: "Surveys are only available on iOS 15+") - @objc public var surveysConfig: PostHogSurveysConfig { - get { _surveysConfig } - set { setSurveysConfig(newValue) } - } - - // only internal - var disableReachabilityForTesting: Bool = false - var disableQueueTimerForTesting: Bool = false - // internal - public var storageManager: PostHogStorageManager? - - @objc(apiKey:) - public init( - apiKey: String - ) { - self.apiKey = apiKey - host = URL(string: PostHogConfig.defaultHost)! - } - - @objc(apiKey:host:) - public init( - apiKey: String, - host: String = defaultHost - ) { - self.apiKey = apiKey - self.host = URL(string: host) ?? URL(string: PostHogConfig.defaultHost)! - } - - /// Returns an array of integrations to be installed based on current configuration - func getIntegrations() -> [PostHogIntegration] { - var integrations: [PostHogIntegration] = [] - - if captureScreenViews { - integrations.append(PostHogScreenViewIntegration()) - } - - if captureApplicationLifecycleEvents { - integrations.append(PostHogAppLifeCycleIntegration()) - } - - #if os(iOS) - if sessionReplay { - integrations.append(PostHogReplayIntegration()) - } - - if _surveys { - integrations.append(PostHogSurveyIntegration()) - } - - #endif - - #if os(iOS) || targetEnvironment(macCatalyst) - if captureElementInteractions { - integrations.append(PostHogAutocaptureIntegration()) - } - #endif - - return integrations - } - - var _surveys: Bool = true // swiftlint:disable:this identifier_name - private func setSurveys(_ value: Bool) { - // protection against objc API availability warning instead of error - // Unlike swift, which enforces stricter safety rules, objc just displays a warning - if #available(iOS 15.0, *) { - _surveys = value - } - } - - var _surveysConfig: PostHogSurveysConfig = .init() // swiftlint:disable:this identifier_name - private func setSurveysConfig(_ value: PostHogSurveysConfig) { - // protection against objc API availability warning instead of error - // Unlike swift, which enforces stricter safety rules, objc just displays a warning - if #available(iOS 15.0, *) { - _surveysConfig = value - } - } - - /// Hook that allows to sanitize the event - /// The hook is called before the event is cached or sent over the wire - private var beforeSend: BeforeSendBlock = { $0 } - - private static func buildBeforeSendBlock(_ blocks: [BeforeSendBlock]) -> BeforeSendBlock { - { event in - blocks.reduce(event) { event, block in - event.flatMap(block) - } - } - } - - public func setBeforeSend(_ blocks: [BeforeSendBlock]) { - beforeSend = Self.buildBeforeSendBlock(blocks) - } - - public func setBeforeSend(_ blocks: BeforeSendBlock...) { - setBeforeSend(blocks) - } - - @available(*, unavailable, message: "Use setBeforeSend(_ blocks: BeforeSendBlock...) instead") - @objc public func setBeforeSend(_ blocks: [BoxedBeforeSendBlock]) { - setBeforeSend(blocks.map(\.block)) - } - - func runBeforeSend(_ event: PostHogEvent) -> PostHogEvent? { - beforeSend(event) - } -} diff --git a/Pods/PostHog/PostHog/PostHogConsumerPayload.swift b/Pods/PostHog/PostHog/PostHogConsumerPayload.swift deleted file mode 100644 index 923752f..0000000 --- a/Pods/PostHog/PostHog/PostHogConsumerPayload.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// PostHogConsumerPayload.swift -// PostHog -// -// Created by Manoel Aranda Neto on 13.10.23. -// - -import Foundation - -struct PostHogConsumerPayload { - let events: [PostHogEvent] - let completion: (Bool) -> Void -} diff --git a/Pods/PostHog/PostHog/PostHogContext.swift b/Pods/PostHog/PostHog/PostHogContext.swift deleted file mode 100644 index db69b84..0000000 --- a/Pods/PostHog/PostHog/PostHogContext.swift +++ /dev/null @@ -1,411 +0,0 @@ -// -// PostHogContext.swift -// PostHog -// -// Created by Manoel Aranda Neto on 16.10.23. -// - -import Foundation - -#if os(iOS) || os(tvOS) || os(visionOS) - import UIKit -#elseif os(macOS) - import AppKit -#elseif os(watchOS) - import WatchKit -#endif - -class PostHogContext { - @ReadWriteLock - private var screenSize: CGSize? - - #if !os(watchOS) - private let reachability: Reachability? - #endif - - private lazy var theStaticContext: [String: Any] = { - // Properties that do not change over the lifecycle of an application - var properties: [String: Any] = [:] - - let infoDictionary = Bundle.main.infoDictionary - - if let appName = infoDictionary?[kCFBundleNameKey as String] { - properties["$app_name"] = appName - } else if let appName = infoDictionary?["CFBundleDisplayName"] { - properties["$app_name"] = appName - } - if let appVersion = infoDictionary?["CFBundleShortVersionString"] { - properties["$app_version"] = appVersion - } - if let appBuild = infoDictionary?["CFBundleVersion"] { - properties["$app_build"] = appBuild - } - - if Bundle.main.bundleIdentifier != nil { - properties["$app_namespace"] = Bundle.main.bundleIdentifier - } - properties["$device_manufacturer"] = "Apple" - properties["$device_model"] = platform() - - if let deviceType = PostHogContext.deviceType { - properties["$device_type"] = deviceType - } - - properties["$is_emulator"] = PostHogContext.isSimulator - - let isIOSAppOnMac = PostHogContext.isIOSAppOnMac - let isMacCatalystApp = PostHogContext.isMacCatalystApp - - properties["$is_ios_running_on_mac"] = isIOSAppOnMac - properties["$is_mac_catalyst_app"] = isMacCatalystApp - - #if os(iOS) || os(tvOS) || os(visionOS) - let device = UIDevice.current - // use https://github.com/devicekit/DeviceKit - let processInfo = ProcessInfo.processInfo - - if isMacCatalystApp || isIOSAppOnMac { - let underlyingOS = device.systemName - let underlyingOSVersion = device.systemVersion - let macOSVersion = processInfo.operatingSystemVersionString - - if isMacCatalystApp { - let osVersion = ProcessInfo.processInfo.operatingSystemVersion - properties["$os_version"] = "\(osVersion.majorVersion).\(osVersion.minorVersion).\(osVersion.patchVersion)" - } else { - let osVersionString = processInfo.operatingSystemVersionString - if let versionRange = osVersionString.range(of: #"\d+\.\d+\.\d+"#, options: .regularExpression) { - properties["$os_version"] = osVersionString[versionRange] - } else { - // fallback to full version string in case formatting changes - properties["$os_version"] = osVersionString - } - } - // device.userInterfaceIdiom reports .pad here, so we use a static value instead - // - For an app deployable on iPad, the idiom type is always .pad (instead of .mac) - // - // Source: https://developer.apple.com/documentation/apple-silicon/adapting-ios-code-to-run-in-the-macos-environment#Handle-unknown-device-types-gracefully - properties["$os_name"] = "macOS" - properties["$device_name"] = processInfo.hostName - } else { - // use https://github.com/devicekit/DeviceKit - properties["$os_name"] = device.systemName - properties["$os_version"] = device.systemVersion - properties["$device_name"] = device.model - } - #elseif os(macOS) - let deviceName = Host.current().localizedName - if (deviceName?.isEmpty) != nil { - properties["$device_name"] = deviceName - } - let processInfo = ProcessInfo.processInfo - properties["$os_name"] = "macOS" - let osVersion = processInfo.operatingSystemVersion - properties["$os_version"] = "\(osVersion.majorVersion).\(osVersion.minorVersion).\(osVersion.patchVersion)" - #endif - - return properties - }() - - #if !os(watchOS) - init(_ reachability: Reachability?) { - self.reachability = reachability - registerNotifications() - } - #else - init() { - if #available(watchOS 7.0, *) { - registerNotifications() - } else { - onShouldUpdateScreenSize() - } - } - #endif - - deinit { - #if !os(watchOS) - unregisterNotifications() - #else - if #available(watchOS 7.0, *) { - unregisterNotifications() - } - #endif - } - - private lazy var theSdkInfo: [String: Any] = { - var sdkInfo: [String: Any] = [:] - sdkInfo["$lib"] = postHogSdkName - sdkInfo["$lib_version"] = postHogVersion - return sdkInfo - }() - - func staticContext() -> [String: Any] { - theStaticContext - } - - func sdkInfo() -> [String: Any] { - theSdkInfo - } - - private func platform() -> String { - var sysctlName = "hw.machine" - - // In case of mac catalyst or iOS running on mac: - // - "hw.machine" returns underlying iPad/iPhone model - // - "hw.model" returns mac model - #if targetEnvironment(macCatalyst) - sysctlName = "hw.model" - #elseif os(iOS) || os(visionOS) - if #available(iOS 14.0, *) { - if ProcessInfo.processInfo.isiOSAppOnMac { - sysctlName = "hw.model" - } - } - #endif - - var size = 0 - sysctlbyname(sysctlName, nil, &size, nil, 0) - var machine = [CChar](repeating: 0, count: size) - sysctlbyname(sysctlName, &machine, &size, nil, 0) - return String(cString: machine) - } - - func dynamicContext() -> [String: Any] { - var properties: [String: Any] = [:] - - if let screenSize { - properties["$screen_width"] = Float(screenSize.width) - properties["$screen_height"] = Float(screenSize.height) - } - - if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) { - if let languageCode = Locale.current.language.languageCode { - properties["$locale"] = languageCode.identifier - } - } else { - if Locale.current.languageCode != nil { - properties["$locale"] = Locale.current.languageCode - } - } - properties["$timezone"] = TimeZone.current.identifier - - #if !os(watchOS) - if reachability != nil { - properties["$network_wifi"] = reachability?.connection == .wifi - properties["$network_cellular"] = reachability?.connection == .cellular - } - #endif - - return properties - } - - /// Returns person properties context by extracting relevant properties from static context. - /// This centralizes the logic for determining which properties should be used as person properties. - func personPropertiesContext() -> [String: Any] { - let staticCtx = staticContext() - var personProperties: [String: Any] = [:] - - // App information - if let appVersion = staticCtx["$app_version"] { - personProperties["$app_version"] = appVersion - } - if let appBuild = staticCtx["$app_build"] { - personProperties["$app_build"] = appBuild - } - - // Operating system information - if let osName = staticCtx["$os_name"] { - personProperties["$os_name"] = osName - } - if let osVersion = staticCtx["$os_version"] { - personProperties["$os_version"] = osVersion - } - - // Device information - if let deviceType = staticCtx["$device_type"] { - personProperties["$device_type"] = deviceType - } - if let deviceManufacturer = staticCtx["$device_manufacturer"] { - personProperties["$device_manufacturer"] = deviceManufacturer - } - if let deviceModel = staticCtx["$device_model"] { - personProperties["$device_model"] = deviceModel - } - - // Localization - read directly to avoid expensive dynamicContext call - if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) { - if let languageCode = Locale.current.language.languageCode { - personProperties["$locale"] = languageCode.identifier - } - } else { - if let languageCode = Locale.current.languageCode { - personProperties["$locale"] = languageCode - } - } - - return personProperties - } - - private func registerNotifications() { - #if os(iOS) || os(tvOS) || os(visionOS) - #if os(iOS) - NotificationCenter.default.addObserver(self, - selector: #selector(onOrientationDidChange), - name: UIDevice.orientationDidChangeNotification, - object: nil) - #endif - NotificationCenter.default.addObserver(self, - selector: #selector(onShouldUpdateScreenSize), - name: UIWindow.didBecomeKeyNotification, - object: nil) - #elseif os(macOS) - NotificationCenter.default.addObserver(self, - selector: #selector(onShouldUpdateScreenSize), - name: NSWindow.didBecomeKeyNotification, - object: nil) - NotificationCenter.default.addObserver(self, - selector: #selector(onShouldUpdateScreenSize), - name: NSWindow.didChangeScreenNotification, - object: nil) - NotificationCenter.default.addObserver(self, - selector: #selector(onShouldUpdateScreenSize), - name: NSApplication.didBecomeActiveNotification, - object: nil) - #elseif os(watchOS) - if #available(watchOS 7.0, *) { - NotificationCenter.default.addObserver(self, - selector: #selector(onShouldUpdateScreenSize), - name: WKApplication.didBecomeActiveNotification, - object: nil) - } - #endif - } - - private func unregisterNotifications() { - #if os(iOS) || os(tvOS) || os(visionOS) - #if os(iOS) - NotificationCenter.default.removeObserver(self, - name: UIDevice.orientationDidChangeNotification, - object: nil) - #endif - NotificationCenter.default.removeObserver(self, - name: UIWindow.didBecomeKeyNotification, - object: nil) - - #elseif os(macOS) - NotificationCenter.default.removeObserver(self, - name: NSWindow.didBecomeKeyNotification, - object: nil) - NotificationCenter.default.removeObserver(self, - name: NSWindow.didChangeScreenNotification, - object: nil) - NotificationCenter.default.removeObserver(self, - name: NSApplication.didBecomeActiveNotification, - object: nil) - #elseif os(watchOS) - if #available(watchOS 7.0, *) { - NotificationCenter.default.removeObserver(self, - name: WKApplication.didBecomeActiveNotification, - object: nil) - } - #endif - } - - /// Retrieves the current screen size of the application window based on platform - private func getScreenSize() -> CGSize? { - #if os(iOS) || os(tvOS) || os(visionOS) - return UIApplication.getCurrentWindow(filterForegrounded: false)?.bounds.size - #elseif os(macOS) - // NSScreen.frame represents the full screen rectangle and includes any space occupied by menu, dock or camera bezel - return NSApplication.shared.windows.first { $0.isKeyWindow }?.screen?.frame.size - #elseif os(watchOS) - return WKInterfaceDevice.current().screenBounds.size - #else - return nil - #endif - } - - #if os(iOS) - // Special treatment for `orientationDidChangeNotification` since the notification seems to be _sometimes_ called early, before screen bounds are flipped - @objc private func onOrientationDidChange() { - updateScreenSize { - self.getScreenSize().map { size in - // manually set width and height based on device orientation. (Needed for fast orientation changes) - if UIDevice.current.orientation.isLandscape { - CGSize(width: max(size.width, size.height), height: min(size.height, size.width)) - } else { - CGSize(width: min(size.width, size.height), height: max(size.height, size.width)) - } - } - } - } - #endif - - @objc private func onShouldUpdateScreenSize() { - updateScreenSize(getScreenSize) - } - - private func updateScreenSize(_ getSize: @escaping () -> CGSize?) { - let block = { - self.screenSize = getSize() - } - // ensure block is executed on `main` since closure accesses non thread-safe UI objects like UIApplication - if Thread.isMainThread { - block() - } else { - DispatchQueue.main.async(execute: block) - } - } - - static let deviceType: String? = { - #if os(iOS) || os(tvOS) - if isMacCatalystApp || isIOSAppOnMac { - return "Desktop" - } else { - switch UIDevice.current.userInterfaceIdiom { - case UIUserInterfaceIdiom.phone: - return "Mobile" - case UIUserInterfaceIdiom.pad: - return "Tablet" - case UIUserInterfaceIdiom.tv: - return "TV" - case UIUserInterfaceIdiom.carPlay: - return "CarPlay" - case UIUserInterfaceIdiom.mac: - return "Desktop" - case UIUserInterfaceIdiom.vision: - return "Vision" - default: - return nil - } - } - #elseif os(macOS) - return "Desktop" - #else - return nil - #endif - }() - - static let isIOSAppOnMac: Bool = { - if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) { - return ProcessInfo.processInfo.isiOSAppOnMac - } - return false - }() - - static let isMacCatalystApp: Bool = { - #if targetEnvironment(macCatalyst) - true - #else - false - #endif - }() - - static let isSimulator: Bool = { - #if targetEnvironment(simulator) - true - #else - false - #endif - }() -} diff --git a/Pods/PostHog/PostHog/PostHogExtensions.swift b/Pods/PostHog/PostHog/PostHogExtensions.swift deleted file mode 100644 index 4f701e6..0000000 --- a/Pods/PostHog/PostHog/PostHogExtensions.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// PostHogExtensions.swift -// PostHog -// -// Created by Manoel Aranda Neto on 13.10.23. -// - -import Foundation - -/** - # Notifications - - This helper module encapsulates all notifications that we trigger from within the SDK. - - */ - -public extension PostHogSDK { - @objc static let didStartNotification = Notification.Name("PostHogDidStart") // object: nil - @objc static let didReceiveFeatureFlags = Notification.Name("PostHogDidReceiveFeatureFlags") // object: nil -} diff --git a/Pods/PostHog/PostHog/PostHogFileBackedQueue.swift b/Pods/PostHog/PostHog/PostHogFileBackedQueue.swift deleted file mode 100644 index 4b6edfb..0000000 --- a/Pods/PostHog/PostHog/PostHogFileBackedQueue.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// PostHogFileBackedQueue.swift -// PostHog -// -// Created by Manoel Aranda Neto on 13.10.23. -// - -import Foundation - -class PostHogFileBackedQueue { - let queue: URL - @ReadWriteLock - private var items = [String]() - - var depth: Int { - items.count - } - - init(queue: URL, oldQueue: URL? = nil) { - self.queue = queue - setup(oldQueue: oldQueue) - } - - private func setup(oldQueue: URL?) { - do { - try FileManager.default.createDirectory(atPath: queue.path, withIntermediateDirectories: true) - } catch { - hedgeLog("Error trying to create caching folder \(error)") - } - - if oldQueue != nil { - migrateOldQueue(queue: queue, oldQueue: oldQueue!) - } - - do { - items = try FileManager.default.contentsOfDirectory(atPath: queue.path) - items.sort { Double($0)! < Double($1)! } - } catch { - hedgeLog("Failed to load files for queue \(error)") - // failed to read directory – bad permissions, perhaps? - } - } - - func peek(_ count: Int) -> [Data] { - loadFiles(count) - } - - func delete(index: Int) { - if items.isEmpty { return } - let removed = items.remove(at: index) - - deleteSafely(queue.appendingPathComponent(removed)) - } - - func pop(_ count: Int) { - deleteFiles(count) - } - - func add(_ contents: Data) { - do { - let filename = "\(Date().timeIntervalSince1970)" - try contents.write(to: queue.appendingPathComponent(filename)) - items.append(filename) - } catch { - hedgeLog("Could not write file \(error)") - } - } - - /// Internal, used for testing - func clear() { - deleteSafely(queue) - setup(oldQueue: nil) - } - - private func loadFiles(_ count: Int) -> [Data] { - var results = [Data]() - - for item in items { - let itemURL = queue.appendingPathComponent(item) - do { - if !FileManager.default.fileExists(atPath: itemURL.path) { - hedgeLog("File \(itemURL) does not exist") - continue - } - let contents = try Data(contentsOf: itemURL) - - results.append(contents) - } catch { - hedgeLog("File \(itemURL) is corrupted \(error)") - - deleteSafely(itemURL) - } - - if results.count == count { - return results - } - } - - return results - } - - private func deleteFiles(_ count: Int) { - for _ in 0 ..< count { - if let removed: String = _items.mutate({ items in - if items.isEmpty { - return nil - } - return items.remove(at: 0) // We always remove from the top of the queue - }) { - deleteSafely(queue.appendingPathComponent(removed)) - } - } - } -} diff --git a/Pods/PostHog/PostHog/PostHogIntegration.swift b/Pods/PostHog/PostHog/PostHogIntegration.swift deleted file mode 100644 index 85c4ae4..0000000 --- a/Pods/PostHog/PostHog/PostHogIntegration.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// PostHogIntegration.swift -// PostHog -// -// Created by Ioannis Josephides on 25/02/2025. -// -import Foundation - -protocol PostHogIntegration { - /** - * Indicates whether this integration requires method swizzling to function. - * - * When `enableSwizzling` is set to `false` in PostHogConfig, integrations - * that return `true` for this property will be skipped during installation. - */ - var requiresSwizzling: Bool { get } - - /** - * Installs and initializes the integration with a PostHogSDK instance. - * - * This method should: - * 1. Run checks if needed to ensure that the integration is only installed once - * 2. Initialize any required resources - * 3. Start the integration's functionality - * - * - Parameter postHog: The PostHogSDK instance to integrate with - * - Throws: InternalPostHogError if installation fails (e.g., already installed) - */ - func install(_ postHog: PostHogSDK) throws - - /** - * Uninstalls the integration from a specific PostHogSDK instance. - * - * This method should: - * 1. Stop all integration functionality - * 2. Clean up any resources - * 3. Remove references to the PostHog instance - * - * - Parameter postHog: The PostHog SDK instance to uninstall from - */ - func uninstall(_ postHog: PostHogSDK) - - /** - * Starts the integration's functionality. - * - * Note: This is typically called automatically during installation - * but may be called manually to restart a stopped integration. - */ - func start() - - /** - * Stops the integration's functionality without uninstalling. - * - * Note: This is typically called automatically during uninstallation - * but may be called manually to temporarily suspend the integration - * while maintaining its installation status (e.g manual start/stop for session recording) - */ - func stop() -} diff --git a/Pods/PostHog/PostHog/PostHogLegacyQueue.swift b/Pods/PostHog/PostHog/PostHogLegacyQueue.swift deleted file mode 100644 index f0938c7..0000000 --- a/Pods/PostHog/PostHog/PostHogLegacyQueue.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// PostHogLegacyQueue.swift -// PostHog -// -// Created by Manoel Aranda Neto on 30.10.23. -// - -import Foundation - -// Migrates the Old Queue (v2) to the new Queue (v3) -func migrateOldQueue(queue: URL, oldQueue: URL) { - if !FileManager.default.fileExists(atPath: oldQueue.path) { - return - } - - defer { - deleteSafely(oldQueue) - } - - do { - let data = try Data(contentsOf: oldQueue) - let array = try JSONSerialization.jsonObject(with: data) as? [Any] - - if array == nil { - return - } - - for item in array! { - guard let event = item as? [String: Any] else { - continue - } - let timestamp = event["timestamp"] as? String ?? toISO8601String(Date()) - - let timestampDate = toISO8601Date(timestamp) ?? Date() - - let filename = "\(timestampDate.timeIntervalSince1970)" - - let contents = try JSONSerialization.data(withJSONObject: event) - - try contents.write(to: queue.appendingPathComponent(filename)) - } - } catch { - hedgeLog("Failed to migrate queue \(error)") - } -} diff --git a/Pods/PostHog/PostHog/PostHogPersonProfiles.swift b/Pods/PostHog/PostHog/PostHogPersonProfiles.swift deleted file mode 100644 index ddcaa52..0000000 --- a/Pods/PostHog/PostHog/PostHogPersonProfiles.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// PostHogPersonProfiles.swift -// PostHog -// -// Created by Manoel Aranda Neto on 09.09.24. -// - -import Foundation - -/// Determines the behavior for processing user profiles. -/// - `never`: We won't process persons for any event. This means that anonymous users will not be merged once -/// they sign up or login, so you lose the ability to create funnels that track users from anonymous to identified. -/// All events (including `$identify`) will be sent with `$process_person_profile: False`. -/// - `always`: We will process persons data for all events. -/// - `identifiedOnly`: (default): we will only process persons when you call `identify`, `alias`, and `group`, Anonymous users won't get person profiles. -@objc(PostHogPersonProfiles) public enum PostHogPersonProfiles: Int { - case never - case always - case identifiedOnly -} diff --git a/Pods/PostHog/PostHog/PostHogPropertiesSanitizer.swift b/Pods/PostHog/PostHog/PostHogPropertiesSanitizer.swift deleted file mode 100644 index 789f197..0000000 --- a/Pods/PostHog/PostHog/PostHogPropertiesSanitizer.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// PostHogPropertiesSanitizer.swift -// PostHog -// -// Created by Manoel Aranda Neto on 06.08.24. -// - -import Foundation - -/// Protocol to sanitize the event properties -@objc(PostHogPropertiesSanitizer) public protocol PostHogPropertiesSanitizer { - /// Sanitizes the event properties - /// - Parameter properties: the event properties to sanitize - /// - Returns: the sanitized properties - /// - /// Obs: `inout` cannot be used in Swift protocols, so you need to clone the properties - /// - /// ```swift - /// private class ExampleSanitizer: PostHogPropertiesSanitizer { - /// public func sanitize(_ properties: [String: Any]) -> [String: Any] { - /// var sanitizedProperties = properties - /// // Perform sanitization - /// // For example, removing keys with empty values - /// for (key, value) in properties { - /// if let stringValue = value as? String, stringValue.isEmpty { - /// sanitizedProperties.removeValue(forKey: key) - /// } - /// } - /// return sanitizedProperties - /// } - /// } - /// ``` - @objc func sanitize(_ properties: [String: Any]) -> [String: Any] -} diff --git a/Pods/PostHog/PostHog/PostHogQueue.swift b/Pods/PostHog/PostHog/PostHogQueue.swift deleted file mode 100644 index bd3fd3b..0000000 --- a/Pods/PostHog/PostHog/PostHogQueue.swift +++ /dev/null @@ -1,285 +0,0 @@ -// -// PostHogQueue.swift -// PostHog -// -// Created by Ben White on 06.02.23. -// - -import Foundation - -/** - # Queue - - The queue uses File persistence. This allows us to - 1. Only send events when we have a network connection - 2. Ensure that we can survive app closing or offline situations - 3. Not hold too much in memory - - */ - -class PostHogQueue { - enum PostHogApiEndpoint: Int { - case batch - case snapshot - } - - private let config: PostHogConfig - private let api: PostHogApi - private var paused: Bool = false - private let pausedLock = NSLock() - private var pausedUntil: Date? - private var retryCount: TimeInterval = 0 - #if !os(watchOS) - private let reachability: Reachability? - #endif - - private var isFlushing = false - private let isFlushingLock = NSLock() - private var timer: Timer? - private let timerLock = NSLock() - private let endpoint: PostHogApiEndpoint - private let dispatchQueue: DispatchQueue - - /// Internal, used for testing - var depth: Int { - fileQueue.depth - } - - private let fileQueue: PostHogFileBackedQueue - - #if !os(watchOS) - init(_ config: PostHogConfig, _ storage: PostHogStorage, _ api: PostHogApi, _ endpoint: PostHogApiEndpoint, _ reachability: Reachability?) { - self.config = config - self.api = api - self.reachability = reachability - self.endpoint = endpoint - - switch endpoint { - case .batch: - fileQueue = PostHogFileBackedQueue(queue: storage.url(forKey: .queue), oldQueue: storage.url(forKey: .oldQeueue)) - dispatchQueue = DispatchQueue(label: "com.posthog.Queue", target: .global(qos: .utility)) - case .snapshot: - fileQueue = PostHogFileBackedQueue(queue: storage.url(forKey: .replayQeueue)) - dispatchQueue = DispatchQueue(label: "com.posthog.ReplayQueue", target: .global(qos: .utility)) - } - } - #else - init(_ config: PostHogConfig, _ storage: PostHogStorage, _ api: PostHogApi, _ endpoint: PostHogApiEndpoint) { - self.config = config - self.api = api - self.endpoint = endpoint - - switch endpoint { - case .batch: - fileQueue = PostHogFileBackedQueue(queue: storage.url(forKey: .queue), oldQueue: storage.url(forKey: .oldQeueue)) - dispatchQueue = DispatchQueue(label: "com.posthog.Queue", target: .global(qos: .utility)) - case .snapshot: - fileQueue = PostHogFileBackedQueue(queue: storage.url(forKey: .replayQeueue)) - dispatchQueue = DispatchQueue(label: "com.posthog.ReplayQueue", target: .global(qos: .utility)) - } - } - #endif - - private func eventHandler(_ payload: PostHogConsumerPayload) { - hedgeLog("Sending batch of \(payload.events.count) events to PostHog") - - switch endpoint { - case .batch: - api.batch(events: payload.events) { result in - self.handleResult(result, payload) - } - case .snapshot: - api.snapshot(events: payload.events) { result in - self.handleResult(result, payload) - } - } - } - - private func handleResult(_ result: PostHogBatchUploadInfo, _ payload: PostHogConsumerPayload) { - // -1 means its not anything related to the API but rather network or something else, so we try again - let statusCode = result.statusCode ?? -1 - - var shouldRetry = false - if 300 ... 399 ~= statusCode || statusCode == -1 { - shouldRetry = true - } - - // TODO: https://github.com/PostHog/posthog-android/pull/130 - // fix: reduce batch size if API returns 413 - - if shouldRetry { - retryCount += 1 - let delay = min(retryCount * retryDelay, maxRetryDelay) - pauseFor(seconds: delay) - hedgeLog("Pausing queue consumption for \(delay) seconds due to \(retryCount) API failure(s).") - } else { - retryCount = 0 - } - - payload.completion(!shouldRetry) - } - - func start(disableReachabilityForTesting: Bool, - disableQueueTimerForTesting: Bool) - { - if !disableReachabilityForTesting { - // Setup the monitoring of network status for the queue - #if !os(watchOS) - reachability?.whenReachable = { reachability in - self.pausedLock.withLock { - if self.config.dataMode == .wifi, reachability.connection != .wifi { - hedgeLog("Queue is paused because its not in WiFi mode") - self.paused = true - } else { - self.paused = false - } - } - - // Always trigger a flush when we are on wifi - if reachability.connection == .wifi { - if !self.isFlushing { - self.flush() - } - } - } - - reachability?.whenUnreachable = { _ in - self.pausedLock.withLock { - hedgeLog("Queue is paused because network is unreachable") - self.paused = true - } - } - - do { - try reachability?.startNotifier() - } catch { - hedgeLog("Error: Unable to monitor network reachability: \(error)") - } - #endif - } - - if !disableQueueTimerForTesting { - timerLock.withLock { - DispatchQueue.main.async { - self.timer = Timer.scheduledTimer(withTimeInterval: self.config.flushIntervalSeconds, repeats: true, block: { _ in - if !self.isFlushing { - self.flush() - } - }) - } - } - } - } - - /// Internal, used for testing - func clear() { - fileQueue.clear() - } - - func stop() { - timerLock.withLock { - timer?.invalidate() - timer = nil - } - } - - func flush() { - if !canFlush() { - return - } - - take(config.maxBatchSize) { payload in - if !payload.events.isEmpty { - self.eventHandler(payload) - } else { - // there's nothing to be sent - payload.completion(true) - } - } - } - - private func flushIfOverThreshold() { - if fileQueue.depth >= config.flushAt { - flush() - } - } - - func add(_ event: PostHogEvent) { - if fileQueue.depth >= config.maxQueueSize { - hedgeLog("Queue is full, dropping oldest event") - // first is always oldest - fileQueue.delete(index: 0) - } - - var data: Data? - do { - data = try JSONSerialization.data(withJSONObject: event.toJSON()) - } catch { - hedgeLog("Tried to queue unserialisable PostHogEvent \(error)") - return - } - - fileQueue.add(data!) - hedgeLog("Queued event '\(event.event)'. Depth: \(fileQueue.depth)") - flushIfOverThreshold() - } - - private func take(_ count: Int, completion: @escaping (PostHogConsumerPayload) -> Void) { - dispatchQueue.async { - self.isFlushingLock.withLock { - if self.isFlushing { - return - } - self.isFlushing = true - } - - let items = self.fileQueue.peek(count) - - var processing = [PostHogEvent]() - - for item in items { - // each element is a PostHogEvent if fromJSON succeeds - guard let event = PostHogEvent.fromJSON(item) else { - continue - } - processing.append(event) - } - - completion(PostHogConsumerPayload(events: processing) { success in - if success, items.count > 0 { - self.fileQueue.pop(items.count) - hedgeLog("Completed!") - } - - self.isFlushingLock.withLock { - self.isFlushing = false - } - }) - } - } - - private func pauseFor(seconds: TimeInterval) { - pausedUntil = Date().addingTimeInterval(seconds) - } - - private func canFlush() -> Bool { - if isFlushing { - hedgeLog("Already flushing") - return false - } - - if paused { - // We don't flush data if the queue is paused - hedgeLog("The queue is paused due to the reachability check") - return false - } - - if pausedUntil != nil, pausedUntil! > Date() { - // We don't flush data if the queue is temporarily paused - hedgeLog("The queue is paused until `\(pausedUntil!)`") - return false - } - - return true - } -} diff --git a/Pods/PostHog/PostHog/PostHogRemoteConfig.swift b/Pods/PostHog/PostHog/PostHogRemoteConfig.swift deleted file mode 100644 index ae346a1..0000000 --- a/Pods/PostHog/PostHog/PostHogRemoteConfig.swift +++ /dev/null @@ -1,636 +0,0 @@ -// -// PostHogRemoteConfig.swift -// PostHog -// -// Created by Manoel Aranda Neto on 10.10.23. -// - -import Foundation - -class PostHogRemoteConfig { - private let hasFeatureFlagsKey = "hasFeatureFlags" - - private let config: PostHogConfig - private let storage: PostHogStorage - private let api: PostHogApi - private let getDefaultPersonProperties: () -> [String: Any] - - private let loadingFeatureFlagsLock = NSLock() - private let featureFlagsLock = NSLock() - private var loadingFeatureFlags = false - private var sessionReplayFlagActive = false - - private var flags: [String: Any]? - private var featureFlags: [String: Any]? - - private var remoteConfigLock = NSLock() - private let loadingRemoteConfigLock = NSLock() - private var loadingRemoteConfig = false - private var remoteConfig: [String: Any]? - private var remoteConfigDidFetch: Bool = false - private var featureFlagPayloads: [String: Any]? - private var requestId: String? - - private let personPropertiesForFlagsLock = NSLock() - private var personPropertiesForFlags: [String: Any] = [:] - - private let groupPropertiesForFlagsLock = NSLock() - private var groupPropertiesForFlags: [String: [String: Any]] = [:] - - /// Internal, only used for testing - var canReloadFlagsForTesting = true - - var onRemoteConfigLoaded: (([String: Any]?) -> Void)? - var onFeatureFlagsLoaded: (([String: Any]?) -> Void)? - - private let dispatchQueue = DispatchQueue(label: "com.posthog.RemoteConfig", - target: .global(qos: .utility)) - - var lastRequestId: String? { - featureFlagsLock.withLock { - requestId ?? storage.getString(forKey: .requestId) - } - } - - init(_ config: PostHogConfig, - _ storage: PostHogStorage, - _ api: PostHogApi, - _ getDefaultPersonProperties: @escaping () -> [String: Any]) - { - self.config = config - self.storage = storage - self.api = api - self.getDefaultPersonProperties = getDefaultPersonProperties - - // Load cached person and group properties for flags - loadCachedPropertiesForFlags() - - preloadSessionReplayFlag() - - if config.remoteConfig { - preloadRemoteConfig() - } else if config.preloadFeatureFlags { - preloadFeatureFlags() - } - } - - private func preloadRemoteConfig() { - remoteConfigLock.withLock { - // load disk cached config to memory - _ = getCachedRemoteConfig() - } - - // may have already beed fetched from `loadFeatureFlags` call - if remoteConfigLock.withLock({ - self.remoteConfig == nil || !self.remoteConfigDidFetch - }) { - dispatchQueue.async { - self.reloadRemoteConfig { [weak self] remoteConfig in - guard let self else { return } - - // if there's no remote config response, skip - guard let remoteConfig else { - hedgeLog("Remote config response is missing, skipping loading flags") - notifyFeatureFlags(nil) - return - } - - // Check if the server explicitly responded with hasFeatureFlags key - if let hasFeatureFlagsBoolValue = remoteConfig[self.hasFeatureFlagsKey] as? Bool, !hasFeatureFlagsBoolValue { - hedgeLog("hasFeatureFlags is false, clearing flags and skipping loading flags") - // Server responded with explicit hasFeatureFlags: false, meaning no active flags on the account - clearFeatureFlags() - // need to notify cause people may be waiting for flags to load - notifyFeatureFlags([:]) - } else if self.config.preloadFeatureFlags { - // If we reach here, hasFeatureFlags is either true, nil or not a boolean value - // Note: notifyFeatureFlags() will be eventually called inside preloadFeatureFlags() - self.preloadFeatureFlags() - } - } - } - } - } - - private func preloadFeatureFlags() { - featureFlagsLock.withLock { - // load disk cached config to memory - _ = getCachedFeatureFlags() - } - - if config.preloadFeatureFlags { - dispatchQueue.async { - self.reloadFeatureFlags() - } - } - } - - func reloadRemoteConfig( - callback: (([String: Any]?) -> Void)? = nil - ) { - guard config.remoteConfig else { - callback?(nil) - return - } - - loadingRemoteConfigLock.withLock { - if self.loadingRemoteConfig { - return - } - self.loadingRemoteConfig = true - } - - api.remoteConfig { config, _ in - if let config { - // cache config - self.remoteConfigLock.withLock { - self.remoteConfig = config - self.storage.setDictionary(forKey: .remoteConfig, contents: config) - } - - // process session replay config - #if os(iOS) - let featureFlags = self.featureFlagsLock.withLock { self.featureFlags } - self.processSessionRecordingConfig(config, featureFlags: featureFlags ?? [:]) - #endif - - // notify - DispatchQueue.main.async { - self.onRemoteConfigLoaded?(config) - } - } - - self.loadingRemoteConfigLock.withLock { - self.remoteConfigDidFetch = true - self.loadingRemoteConfig = false - } - - callback?(config) - } - } - - func reloadFeatureFlags( - callback: (([String: Any]?) -> Void)? = nil - ) { - guard canReloadFlagsForTesting else { - return - } - - guard let storageManager = config.storageManager else { - hedgeLog("No PostHogStorageManager found in config, skipping loading feature flags") - callback?(nil) - return - } - - let groups = featureFlagsLock.withLock { getGroups() } - let distinctId = storageManager.getDistinctId() - let anonymousId = config.reuseAnonymousId == false ? storageManager.getAnonymousId() : nil - - loadFeatureFlags( - distinctId: distinctId, - anonymousId: anonymousId, - groups: groups, - callback: callback ?? { _ in } - ) - } - - private func preloadSessionReplayFlag() { - var sessionReplay: [String: Any]? - var featureFlags: [String: Any]? - featureFlagsLock.withLock { - sessionReplay = self.storage.getDictionary(forKey: .sessionReplay) as? [String: Any] - featureFlags = self.getCachedFeatureFlags() - } - - if let sessionReplay = sessionReplay { - sessionReplayFlagActive = isRecordingActive(featureFlags ?? [:], sessionReplay) - - if let endpoint = sessionReplay["endpoint"] as? String { - config.snapshotEndpoint = endpoint - } - } - } - - private func isRecordingActive(_ featureFlags: [String: Any], _ sessionRecording: [String: Any]) -> Bool { - var recordingActive = true - - // check for boolean flags - if let linkedFlag = sessionRecording["linkedFlag"] as? String { - let value = featureFlags[linkedFlag] - - if let boolValue = value as? Bool { - // boolean flag with value - recordingActive = boolValue - } else if value is String { - // its a multi-variant flag linked to "any" - recordingActive = true - } else { - // disable recording if the flag does not exist/quota limited - recordingActive = false - } - // check for specific flag variant - } else if let linkedFlag = sessionRecording["linkedFlag"] as? [String: Any] { - let flag = linkedFlag["flag"] as? String - let variant = linkedFlag["variant"] as? String - - if let flag, let variant { - let value = featureFlags[flag] as? String - recordingActive = value == variant - } else { - // disable recording if the flag does not exist/quota limited - recordingActive = false - } - } - // check for multi flag variant (any) - // if let linkedFlag = sessionRecording["linkedFlag"] as? String, - // featureFlags[linkedFlag] != nil - // is also a valid check but since we cannot check the value of the flag, - // we consider session recording is active - - return recordingActive - } - - func loadFeatureFlags( - distinctId: String, - anonymousId: String?, - groups: [String: String], - callback: @escaping ([String: Any]?) -> Void - ) { - loadingFeatureFlagsLock.withLock { - if self.loadingFeatureFlags { - return - } - self.loadingFeatureFlags = true - } - - let personProperties = getPersonPropertiesForFlags() - let groupProperties = getGroupPropertiesForFlags() - - api.flags(distinctId: distinctId, - anonymousId: anonymousId, - groups: groups, - personProperties: personProperties, - groupProperties: groupProperties.isEmpty ? nil : groupProperties) - { data, _ in - self.dispatchQueue.async { - // Check for quota limitation first - if let quotaLimited = data?["quotaLimited"] as? [String], - quotaLimited.contains("feature_flags") - { - // swiftlint:disable:next line_length - hedgeLog("Warning: Feature flags quota limit reached - clearing all feature flags and payloads. See https://posthog.com/docs/billing/limits-alerts for more information.") - - self.clearFeatureFlags() - self.notifyFeatureFlagsAndRelease([:]) - return callback([:]) - } - - // Safely handle optional data - guard var data = data else { - hedgeLog("Error: Flags response data is nil") - self.notifyFeatureFlagsAndRelease(nil) - return callback(nil) - } - - self.normalizeResponse(&data) - - let flagsV4 = data["flags"] as? [String: Any] - - guard let featureFlags = data["featureFlags"] as? [String: Any], - let featureFlagPayloads = data["featureFlagPayloads"] as? [String: Any] - else { - hedgeLog("Error: Flags response missing correct featureFlags format") - self.notifyFeatureFlagsAndRelease(nil) - return callback(nil) - } - - #if os(iOS) - self.processSessionRecordingConfig(data, featureFlags: featureFlags) - #endif - - // Grab the request ID from the response - let requestId = data["requestId"] as? String - let errorsWhileComputingFlags = data["errorsWhileComputingFlags"] as? Bool ?? false - var loadedFeatureFlags: [String: Any]? - - self.featureFlagsLock.withLock { - if let requestId { - // Store the request ID in the storage. - self.setCachedRequestId(requestId) - } - - if errorsWhileComputingFlags { - // v4 cached flags which contains metadata about each flag. - let cachedFlags = self.getCachedFlags() ?? [:] - - // The following two aren't necessarily needed for v4, but we'll keep them for now - // for back compatibility for existing v3 users who might already have cached flag data. - let cachedFeatureFlags = self.getCachedFeatureFlags() ?? [:] - let cachedFeatureFlagsPayloads = self.getCachedFeatureFlagPayload() ?? [:] - - let newFeatureFlags = cachedFeatureFlags.merging(featureFlags) { _, new in new } - let newFeatureFlagsPayloads = cachedFeatureFlagsPayloads.merging(featureFlagPayloads) { _, new in new } - - // if not all flags were computed, we upsert flags instead of replacing them - loadedFeatureFlags = newFeatureFlags - if let flagsV4 { - let newFlags = cachedFlags.merging(flagsV4) { _, new in new } - // if not all flags were computed, we upsert flags instead of replacing them - self.setCachedFlags(newFlags) - } - self.setCachedFeatureFlags(newFeatureFlags) - self.setCachedFeatureFlagPayload(newFeatureFlagsPayloads) - self.notifyFeatureFlagsAndRelease(newFeatureFlags) - } else { - loadedFeatureFlags = featureFlags - if let flagsV4 { - self.setCachedFlags(flagsV4) - } - self.setCachedFeatureFlags(featureFlags) - self.setCachedFeatureFlagPayload(featureFlagPayloads) - self.notifyFeatureFlagsAndRelease(featureFlags) - } - } - - return callback(loadedFeatureFlags) - } - } - } - - #if os(iOS) - private func processSessionRecordingConfig(_ data: [String: Any]?, featureFlags: [String: Any]) { - if let sessionRecording = data?["sessionRecording"] as? Bool { - sessionReplayFlagActive = sessionRecording - - // its always false here anyway - if !sessionRecording { - storage.remove(key: .sessionReplay) - } - - } else if let sessionRecording = data?["sessionRecording"] as? [String: Any] { - // keeps the value from config.sessionReplay since having sessionRecording - // means its enabled on the project settings, but its only enabled - // when local replay integration is enabled/active - if let endpoint = sessionRecording["endpoint"] as? String { - config.snapshotEndpoint = endpoint - } - sessionReplayFlagActive = isRecordingActive(featureFlags, sessionRecording) - storage.setDictionary(forKey: .sessionReplay, contents: sessionRecording) - } - } - #endif - - private func notifyFeatureFlags(_ featureFlags: [String: Any]?) { - DispatchQueue.main.async { - self.onFeatureFlagsLoaded?(featureFlags) - NotificationCenter.default.post(name: PostHogSDK.didReceiveFeatureFlags, object: nil) - } - } - - private func notifyFeatureFlagsAndRelease(_ featureFlags: [String: Any]?) { - notifyFeatureFlags(featureFlags) - - loadingFeatureFlagsLock.withLock { - self.loadingFeatureFlags = false - } - } - - func getFeatureFlags() -> [String: Any]? { - featureFlagsLock.withLock { getCachedFeatureFlags() } - } - - func getFeatureFlag(_ key: String) -> Any? { - var flags: [String: Any]? - featureFlagsLock.withLock { - flags = self.getCachedFeatureFlags() - } - - return flags?[key] - } - - func getFeatureFlagDetails(_ key: String) -> Any? { - var flags: [String: Any]? - featureFlagsLock.withLock { - flags = self.getCachedFlags() - } - - return flags?[key] - } - - // To be called after acquiring `featureFlagsLock` - private func getCachedFeatureFlagPayload() -> [String: Any]? { - if featureFlagPayloads == nil { - featureFlagPayloads = storage.getDictionary(forKey: .enabledFeatureFlagPayloads) as? [String: Any] - } - return featureFlagPayloads - } - - // To be called after acquiring `featureFlagsLock` - private func setCachedFeatureFlagPayload(_ featureFlagPayloads: [String: Any]) { - self.featureFlagPayloads = featureFlagPayloads - storage.setDictionary(forKey: .enabledFeatureFlagPayloads, contents: featureFlagPayloads) - } - - // To be called after acquiring `featureFlagsLock` - private func getCachedFeatureFlags() -> [String: Any]? { - if featureFlags == nil { - featureFlags = storage.getDictionary(forKey: .enabledFeatureFlags) as? [String: Any] - } - return featureFlags - } - - // To be called after acquiring `featureFlagsLock` - private func setCachedFeatureFlags(_ featureFlags: [String: Any]) { - self.featureFlags = featureFlags - storage.setDictionary(forKey: .enabledFeatureFlags, contents: featureFlags) - } - - // To be called after acquiring `featureFlagsLock` - private func setCachedFlags(_ flags: [String: Any]) { - self.flags = flags - storage.setDictionary(forKey: .flags, contents: flags) - } - - // To be called after acquiring `featureFlagsLock` - private func getCachedFlags() -> [String: Any]? { - if flags == nil { - flags = storage.getDictionary(forKey: .flags) as? [String: Any] - } - return flags - } - - func setPersonPropertiesForFlags(_ properties: [String: Any]) { - personPropertiesForFlagsLock.withLock { - // Merge properties additively, similar to JS SDK behavior - personPropertiesForFlags.merge(properties, uniquingKeysWith: { _, new in new }) - // Persist to disk - storage.setDictionary(forKey: .personPropertiesForFlags, contents: personPropertiesForFlags) - } - } - - func resetPersonPropertiesForFlags() { - personPropertiesForFlagsLock.withLock { - personPropertiesForFlags.removeAll() - // Clear from disk - storage.setDictionary(forKey: .personPropertiesForFlags, contents: personPropertiesForFlags) - } - } - - func setGroupPropertiesForFlags(_ groupType: String, properties: [String: Any]) { - groupPropertiesForFlagsLock.withLock { - // Merge properties additively for this group type - groupPropertiesForFlags[groupType, default: [:]].merge(properties) { _, new in new } - // Persist to disk - storage.setDictionary(forKey: .groupPropertiesForFlags, contents: groupPropertiesForFlags) - } - } - - func resetGroupPropertiesForFlags(_ groupType: String? = nil) { - groupPropertiesForFlagsLock.withLock { - if let groupType = groupType { - groupPropertiesForFlags.removeValue(forKey: groupType) - } else { - groupPropertiesForFlags.removeAll() - } - // Persist changes to disk - storage.setDictionary(forKey: .groupPropertiesForFlags, contents: groupPropertiesForFlags) - } - } - - private func getGroupPropertiesForFlags() -> [String: [String: Any]] { - groupPropertiesForFlagsLock.withLock { - groupPropertiesForFlags - } - } - - private func getPersonPropertiesForFlags() -> [String: Any] { - personPropertiesForFlagsLock.withLock { - var properties = personPropertiesForFlags - - // Always include fresh default properties if enabled - if config.setDefaultPersonProperties { - let defaultProperties = getDefaultPersonProperties() - // User-set properties override default properties - properties = defaultProperties.merging(properties) { _, userValue in userValue } - } - - return properties - } - } - - private func loadCachedPropertiesForFlags() { - personPropertiesForFlagsLock.withLock { - if let cachedPersonProperties = storage.getDictionary(forKey: .personPropertiesForFlags) as? [String: Any] { - personPropertiesForFlags = cachedPersonProperties - } - } - - groupPropertiesForFlagsLock.withLock { - if let cachedGroupProperties = storage.getDictionary(forKey: .groupPropertiesForFlags) as? [String: [String: Any]] { - groupPropertiesForFlags = cachedGroupProperties - } - } - } - - func getFeatureFlagPayload(_ key: String) -> Any? { - var flags: [String: Any]? - featureFlagsLock.withLock { - flags = getCachedFeatureFlagPayload() - } - - let value = flags?[key] - - guard let stringValue = value as? String else { - return value - } - - do { - // The payload value is stored as a string and is not pre-parsed... - // We need to mimic the JSON.parse of JS which is what posthog-js uses - return try JSONSerialization.jsonObject(with: stringValue.data(using: .utf8)!, options: .fragmentsAllowed) - } catch { - hedgeLog("Error parsing the object \(String(describing: value)): \(error)") - } - - // fallback to original value if not possible to serialize - return value - } - - // To be called after acquiring `featureFlagsLock` - private func setCachedRequestId(_ value: String?) { - requestId = value - if let value { - storage.setString(forKey: .requestId, contents: value) - } else { - storage.remove(key: .requestId) - } - } - - private func normalizeResponse(_ data: inout [String: Any]) { - if let flagsV4 = data["flags"] as? [String: Any] { - var featureFlags = [String: Any]() - var featureFlagsPayloads = [String: Any]() - for (key, value) in flagsV4 { - if let flag = value as? [String: Any] { - if let variant = flag["variant"] as? String { - featureFlags[key] = variant - // If there's a variant, the flag is enabled, so we can store the payload - if let metadata = flag["metadata"] as? [String: Any], - let payload = metadata["payload"] - { - featureFlagsPayloads[key] = payload - } - } else { - let enabled = flag["enabled"] as? Bool - featureFlags[key] = enabled - - // Only store payload if the flag is enabled - if enabled == true, - let metadata = flag["metadata"] as? [String: Any], - let payload = metadata["payload"] - { - featureFlagsPayloads[key] = payload - } - } - } - } - data["featureFlags"] = featureFlags - data["featureFlagPayloads"] = featureFlagsPayloads - } - } - - private func clearFeatureFlags() { - featureFlagsLock.withLock { - setCachedFlags([:]) - setCachedFeatureFlags([:]) - setCachedFeatureFlagPayload([:]) - setCachedRequestId(nil) // requestId no longer valid - } - } - - #if os(iOS) - func isSessionReplayFlagActive() -> Bool { - sessionReplayFlagActive - } - #endif - - private func getGroups() -> [String: String] { - guard let groups = storage.getDictionary(forKey: .groups) as? [String: String] else { - return [:] - } - return groups - } - - // MARK: Remote Config - - func getRemoteConfig() -> [String: Any]? { - remoteConfigLock.withLock { getCachedRemoteConfig() } - } - - private func getCachedRemoteConfig() -> [String: Any]? { - if remoteConfig == nil { - remoteConfig = storage.getDictionary(forKey: .remoteConfig) as? [String: Any] - } - return remoteConfig - } -} diff --git a/Pods/PostHog/PostHog/PostHogSDK.swift b/Pods/PostHog/PostHog/PostHogSDK.swift deleted file mode 100644 index 182ac02..0000000 --- a/Pods/PostHog/PostHog/PostHogSDK.swift +++ /dev/null @@ -1,1499 +0,0 @@ -// swiftlint:disable file_length cyclomatic_complexity - -// -// PostHogSDK.swift -// PostHogSDK -// -// Created by Ben White on 07.02.23. -// - -import Foundation - -#if os(iOS) || os(tvOS) - import UIKit -#elseif os(macOS) - import AppKit -#elseif os(watchOS) - import WatchKit -#endif - -let retryDelay = 5.0 -let maxRetryDelay = 30.0 - -// renamed to PostHogSDK due to https://github.com/apple/swift/issues/56573 -@objc public class PostHogSDK: NSObject { - private(set) var config: PostHogConfig - - private init(_ config: PostHogConfig) { - self.config = config - } - - private var enabled = false - private let setupLock = NSLock() - private let optOutLock = NSLock() - private let groupsLock = NSLock() - private let flagCallReportedLock = NSLock() - private let personPropsLock = NSLock() - - private var queue: PostHogQueue? - private var replayQueue: PostHogQueue? - private(set) var storage: PostHogStorage? - #if !os(watchOS) - private var reachability: Reachability? - #endif - private var flagCallReported = Set() - private(set) var remoteConfig: PostHogRemoteConfig? - private var context: PostHogContext? - private static var apiKeys = Set() - private var installedIntegrations: [PostHogIntegration] = [] - let sessionManager = PostHogSessionManager() - - #if os(iOS) - private weak var replayIntegration: PostHogReplayIntegration? - private weak var surveysIntegration: PostHogSurveyIntegration? - #endif - - // nonisolated(unsafe) is introduced in Swift 5.10 - #if swift(>=5.10) - @objc public nonisolated(unsafe) static let shared: PostHogSDK = { - let instance = PostHogSDK(PostHogConfig(apiKey: "")) - return instance - }() - #else - @objc public static let shared: PostHogSDK = { - let instance = PostHogSDK(PostHogConfig(apiKey: "")) - return instance - }() - #endif - - deinit { - #if !os(watchOS) - self.reachability?.stopNotifier() - #endif - - uninstallIntegrations() - } - - @objc public func debug(_ enabled: Bool = true) { - if !isEnabled() { - return - } - - toggleHedgeLog(enabled) - } - - @objc public func setup(_ config: PostHogConfig) { - setupLock.withLock { - toggleHedgeLog(config.debug) - if enabled { - hedgeLog("Setup called despite already being setup!") - return - } - - if PostHogSDK.apiKeys.contains(config.apiKey) { - hedgeLog("API Key: \(config.apiKey) already has a PostHog instance.") - } else { - PostHogSDK.apiKeys.insert(config.apiKey) - } - - enabled = true - self.config = config - let theStorage = PostHogStorage(config) - storage = theStorage - let api = PostHogApi(config) - - config.storageManager = config.storageManager ?? PostHogStorageManager(config) - remoteConfig = PostHogRemoteConfig(config, theStorage, api) { [weak self] in - self?.getDefaultPersonProperties() ?? [:] - } - - #if !os(watchOS) - do { - reachability = try Reachability() - } catch { - // ignored - } - context = PostHogContext(reachability) - #else - context = PostHogContext() - #endif - - optOutLock.withLock { - let optOut = theStorage.getBool(forKey: .optOut) - config.optOut = optOut ?? config.optOut - } - - #if !os(watchOS) - queue = PostHogQueue(config, theStorage, api, .batch, reachability) - replayQueue = PostHogQueue(config, theStorage, api, .snapshot, reachability) - #else - queue = PostHogQueue(config, theStorage, api, .batch) - replayQueue = PostHogQueue(config, theStorage, api, .snapshot) - #endif - - queue?.start(disableReachabilityForTesting: config.disableReachabilityForTesting, - disableQueueTimerForTesting: config.disableQueueTimerForTesting) - - replayQueue?.start(disableReachabilityForTesting: config.disableReachabilityForTesting, - disableQueueTimerForTesting: config.disableQueueTimerForTesting) - - // Create session manager instance for this PostHogSDK instance - sessionManager.setup(config: config) - sessionManager.startSession() - - if !config.optOut { - // don't install integrations if in opt-out state - installIntegrations() - } - - DispatchQueue.main.async { - NotificationCenter.default.post(name: PostHogSDK.didStartNotification, object: nil) - } - } - } - - @objc public func getDistinctId() -> String { - if !isEnabled() { - return "" - } - - return config.storageManager?.getDistinctId() ?? "" - } - - @objc public func getAnonymousId() -> String { - if !isEnabled() { - return "" - } - - return config.storageManager?.getAnonymousId() ?? "" - } - - @objc public func getSessionId() -> String? { - if !isEnabled() { - return nil - } - - return sessionManager.getSessionId(readOnly: true) - } - - @objc public func startSession() { - if !isEnabled() { - return - } - - sessionManager.startSession() - } - - @objc public func endSession() { - if !isEnabled() { - return - } - - sessionManager.endSession() - } - - // EVENT CAPTURE - - private func dynamicContext() -> [String: Any] { - var properties = getRegisteredProperties() - - var groups: [String: String]? - groupsLock.withLock { - groups = getGroups() - } - if groups != nil, !groups!.isEmpty { - properties["$groups"] = groups! - } - - guard let flags = remoteConfig?.getFeatureFlags() as? [String: Any] else { - return properties - } - - var keys: [String] = [] - for (key, value) in flags { - properties["$feature/\(key)"] = value - - var active = true - let boolValue = value as? Bool - if boolValue != nil { - active = boolValue! - } else { - active = true - } - - if active { - keys.append(key) - } - } - - if !keys.isEmpty { - properties["$active_feature_flags"] = keys - } - - return properties - } - - private func hasPersonProcessing() -> Bool { - !( - config.personProfiles == .never || - ( - config.personProfiles == .identifiedOnly && - config.storageManager?.isIdentified() == false && - config.storageManager?.isPersonProcessing() == false - ) - ) - } - - @discardableResult - private func requirePersonProcessing() -> Bool { - if config.personProfiles == .never { - hedgeLog("personProfiles is set to `never`. This call will be ignored.") - return false - } - config.storageManager?.setPersonProcessing(true) - return true - } - - private func buildProperties(distinctId: String, - properties: [String: Any]?, - userProperties: [String: Any]? = nil, - userPropertiesSetOnce: [String: Any]? = nil, - groups: [String: String]? = nil, - appendSharedProps: Bool = true, - timestamp: Date? = nil) -> [String: Any] - { - var props: [String: Any] = [:] - - if appendSharedProps { - let staticCtx = context?.staticContext() - let dynamicCtx = context?.dynamicContext() - let localDynamicCtx = dynamicContext() - - if staticCtx != nil { - props = props.merging(staticCtx ?? [:]) { current, _ in current } - } - if dynamicCtx != nil { - props = props.merging(dynamicCtx ?? [:]) { current, _ in current } - } - props = props.merging(localDynamicCtx) { current, _ in current } - if userProperties != nil { - props["$set"] = (userProperties ?? [:]) - } - if userPropertiesSetOnce != nil { - props["$set_once"] = (userPropertiesSetOnce ?? [:]) - } - if groups != nil { - // $groups are also set via the dynamicContext - let currentGroups = props["$groups"] as? [String: String] ?? [:] - let mergedGroups = currentGroups.merging(groups ?? [:]) { current, _ in current } - props["$groups"] = mergedGroups - } - - if let isIdentified = config.storageManager?.isIdentified() { - props["$is_identified"] = isIdentified - } - - props["$process_person_profile"] = hasPersonProcessing() - } - - let sdkInfo = context?.sdkInfo() - if sdkInfo != nil { - props = props.merging(sdkInfo ?? [:]) { current, _ in current } - } - - // use existing session id if already present in properties (from params) - // for session replay, we attach the session id on the event as early as possible to avoid sending snapshots to a wrong session - // if not present, get a current or new session id at event timestamp - let propSessionId = properties?["$session_id"] as? String - let sessionId: String? = propSessionId.isNilOrEmpty - ? sessionManager.getSessionId(at: timestamp ?? now()) - : propSessionId - - if let sessionId { - if propSessionId.isNilOrEmpty { - props["$session_id"] = sessionId - } - // only Session replay requires $window_id, so we set as the same as $session_id. - // the backend might fallback to $session_id if $window_id is not present next. - #if os(iOS) - if !appendSharedProps, isSessionReplayActive() { - props["$window_id"] = sessionId - } - #endif - } - - // only Session Replay needs distinct_id also in the props - // remove after https://github.com/PostHog/posthog/issues/23275 gets merged - let propDistinctId = properties?["distinct_id"] as? String - if !appendSharedProps, propDistinctId == nil || propDistinctId?.isEmpty == true { - props["distinct_id"] = distinctId - } - - props = props.merging(properties ?? [:]) { current, _ in current } - - return props - } - - @objc public func flush() { - if !isEnabled() { - return - } - - queue?.flush() - replayQueue?.flush() - } - - @objc public func reset() { - if !isEnabled() { - return - } - - // storage also removes all feature flags - storage?.reset(keepAnonymousId: config.reuseAnonymousId) - config.storageManager?.reset(keepAnonymousId: config.reuseAnonymousId) - flagCallReportedLock.withLock { - flagCallReported.removeAll() - } - sessionManager.reset() - - // Clear person and group properties for flags - remoteConfig?.resetPersonPropertiesForFlags() - remoteConfig?.resetGroupPropertiesForFlags() - - // reload flags as anon user - remoteConfig?.reloadFeatureFlags() - } - - private func getGroups() -> [String: String] { - guard let groups = storage?.getDictionary(forKey: .groups) as? [String: String] else { - return [:] - } - return groups - } - - private func getRegisteredProperties() -> [String: Any] { - guard let props = storage?.getDictionary(forKey: .registerProperties) as? [String: Any] else { - return [:] - } - return props - } - - // register is a reserved word in ObjC - @objc(registerProperties:) - public func register(_ properties: [String: Any]) { - if !isEnabled() { - return - } - - let sanitizedProps = sanitizeDictionary(properties) - if sanitizedProps == nil { - return - } - - personPropsLock.withLock { - let props = getRegisteredProperties() - let mergedProps = props.merging(sanitizedProps!) { _, new in new } - storage?.setDictionary(forKey: .registerProperties, contents: mergedProps) - } - } - - @objc(unregisterProperties:) - public func unregister(_ key: String) { - if !isEnabled() { - return - } - - personPropsLock.withLock { - var props = getRegisteredProperties() - props.removeValue(forKey: key) - storage?.setDictionary(forKey: .registerProperties, contents: props) - } - } - - @objc public func identify(_ distinctId: String) { - identify(distinctId, userProperties: nil, userPropertiesSetOnce: nil) - } - - @objc(identifyWithDistinctId:userProperties:) - public func identify(_ distinctId: String, - userProperties: [String: Any]? = nil) - { - identify(distinctId, userProperties: userProperties, userPropertiesSetOnce: nil) - } - - @objc(identifyWithDistinctId:userProperties:userPropertiesSetOnce:) - public func identify(_ distinctId: String, - userProperties: [String: Any]? = nil, - userPropertiesSetOnce: [String: Any]? = nil) - { - if !isEnabled() { - return - } - - if distinctId.isEmpty { - hedgeLog("identify call not allowed, distinctId is invalid: \(distinctId)") - return - } - - if isOptOutState() { - return - } - - if !requirePersonProcessing() { - return - } - - guard let queue, let storageManager = config.storageManager else { - return - } - let oldDistinctId = getDistinctId() - - let isIdentified = storageManager.isIdentified() - - let hasDifferentDistinctId = distinctId != oldDistinctId - - if hasDifferentDistinctId, !isIdentified { - var props: [String: Any] = ["distinct_id": distinctId] - - if !config.reuseAnonymousId { - // We keep the AnonymousId to be used by flags calls and identify to link the previousId - storageManager.setAnonymousId(oldDistinctId) - props["$anon_distinct_id"] = oldDistinctId - } - - storageManager.setDistinctId(distinctId) - storageManager.setIdentified(true) - - let properties = buildProperties( - distinctId: distinctId, - properties: props, - userProperties: sanitizeDictionary(userProperties), - userPropertiesSetOnce: sanitizeDictionary(userPropertiesSetOnce) - ) - - guard let event = buildEvent(event: "$identify", distinctId: distinctId, properties: properties) else { - return - } - - queue.add(event) - - // Automatically set person properties for feature flags during identify() call - setPersonPropertiesForFlagsIfNeeded(userProperties, userPropertiesSetOnce: userPropertiesSetOnce) - - remoteConfig?.reloadFeatureFlags() - - // we need to make sure the user props update is for the same user - // otherwise they have to reset and identify again - } else if !hasDifferentDistinctId, !(userProperties?.isEmpty ?? true) || !(userPropertiesSetOnce?.isEmpty ?? true) { - capture("$set", - distinctId: distinctId, - userProperties: userProperties, - userPropertiesSetOnce: userPropertiesSetOnce) - - // Automatically set person properties for feature flags during user property updates - setPersonPropertiesForFlagsIfNeeded(userProperties, userPropertiesSetOnce: userPropertiesSetOnce) - - // Note we don't reload flags on property changes as these get processed async - - } else { - hedgeLog("already identified with id: \(oldDistinctId)") - } - } - - private func isOptOutState() -> Bool { - if config.optOut { - hedgeLog("PostHog is in OptOut state.") - return true - } - return false - } - - private func setPersonPropertiesForFlagsIfNeeded( - _ userProperties: [String: Any]?, - userPropertiesSetOnce: [String: Any]? = nil - ) { - guard hasPersonProcessing() else { - return - } - - let sanitizedUserProperties = sanitizeDictionary(userProperties) ?? [:] - let sanitizedUserPropertiesSetOnce = sanitizeDictionary(userPropertiesSetOnce) ?? [:] - - // Combine both types of properties for feature flag evaluation - let allProperties = sanitizedUserProperties.merging(sanitizedUserPropertiesSetOnce) { current, _ in current } - - guard !allProperties.isEmpty else { - return - } - - remoteConfig?.setPersonPropertiesForFlags(allProperties) - } - - private func setGroupPropertiesForFlagsIfNeeded( - type: String, - groupProperties: [String: Any]? - ) { - guard hasPersonProcessing() else { - return - } - - let sanitizedGroupProperties = sanitizeDictionary(groupProperties) ?? [:] - - guard !sanitizedGroupProperties.isEmpty else { - return - } - - remoteConfig?.setGroupPropertiesForFlags(type, properties: sanitizedGroupProperties) - } - - /// Returns fresh default device and app properties for feature flag evaluation. - /// This ensures feature flags can use current properties like $app_version, $os_version, etc. - /// These properties are computed fresh each time they're needed. - private func getDefaultPersonProperties() -> [String: Any] { - guard config.setDefaultPersonProperties else { return [:] } - guard isEnabled() else { return [:] } - - return context?.personPropertiesContext() ?? [:] - } - - @objc public func capture(_ event: String) { - capture(event, distinctId: nil, properties: nil, userProperties: nil, userPropertiesSetOnce: nil, groups: nil) - } - - @objc(captureWithEvent:properties:) - public func capture(_ event: String, - properties: [String: Any]? = nil) - { - capture(event, distinctId: nil, properties: properties, userProperties: nil, userPropertiesSetOnce: nil, groups: nil) - } - - @objc(captureWithEvent:properties:userProperties:) - public func capture(_ event: String, - properties: [String: Any]? = nil, - userProperties: [String: Any]? = nil) - { - capture(event, distinctId: nil, properties: properties, userProperties: userProperties, userPropertiesSetOnce: nil, groups: nil) - } - - @objc(captureWithEvent:properties:userProperties:userPropertiesSetOnce:) - public func capture(_ event: String, - properties: [String: Any]? = nil, - userProperties: [String: Any]? = nil, - userPropertiesSetOnce: [String: Any]? = nil) - { - capture(event, distinctId: nil, properties: properties, userProperties: userProperties, userPropertiesSetOnce: userPropertiesSetOnce, groups: nil) - } - - @objc(captureWithEvent:properties:userProperties:userPropertiesSetOnce:groups:) - public func capture(_ event: String, - properties: [String: Any]? = nil, - userProperties: [String: Any]? = nil, - userPropertiesSetOnce: [String: Any]? = nil, - groups: [String: String]? = nil) - { - capture(event, distinctId: nil, properties: properties, userProperties: userProperties, userPropertiesSetOnce: userPropertiesSetOnce, groups: groups) - } - - @objc(captureWithEvent:distinctId:properties:userProperties:userPropertiesSetOnce:groups:) - public func capture(_ event: String, - distinctId: String? = nil, - properties: [String: Any]? = nil, - userProperties: [String: Any]? = nil, - userPropertiesSetOnce: [String: Any]? = nil, - groups: [String: String]? = nil) - { - capture(event, - distinctId: distinctId, - properties: properties, - userProperties: userProperties, - userPropertiesSetOnce: userPropertiesSetOnce, - groups: groups, - timestamp: nil) - } - - @objc(captureWithEvent:distinctId:properties:userProperties:userPropertiesSetOnce:groups:timestamp:) - public func capture(_ event: String, - distinctId: String? = nil, - properties: [String: Any]? = nil, - userProperties: [String: Any]? = nil, - userPropertiesSetOnce: [String: Any]? = nil, - groups: [String: String]? = nil, - timestamp: Date? = nil) - { - if !isEnabled() { - return - } - - if isOptOutState() { - return - } - - guard let queue else { - return - } - - var isSnapshotEvent = event == "$snapshot" - let eventTimestamp = timestamp ?? now() - let eventDistinctId = distinctId ?? getDistinctId() - - // if the user isn't identified but passed userProperties, userPropertiesSetOnce or groups, - // we should still enable person processing since this is intentional - if userProperties?.isEmpty == false || userPropertiesSetOnce?.isEmpty == false || groups?.isEmpty == false { - requirePersonProcessing() - } - - let properties = buildProperties(distinctId: eventDistinctId, - properties: sanitizeDictionary(properties), - userProperties: sanitizeDictionary(userProperties), - userPropertiesSetOnce: sanitizeDictionary(userPropertiesSetOnce), - groups: groups, - appendSharedProps: !isSnapshotEvent, - timestamp: timestamp) - - // Sanitize is now called in buildEvent - let posthogEvent = buildEvent( - event: event, - distinctId: eventDistinctId, - properties: properties, - timestamp: eventTimestamp - ) - - guard let posthogEvent else { - return - } - - // Reevaluate if this is a snapshot event because the event might have been updated by the beforeSend hook - isSnapshotEvent = posthogEvent.event == "$snapshot" - - // if this is a $snapshot event and $session_id is missing, don't process then event - if isSnapshotEvent, posthogEvent.properties["$session_id"] == nil { - return - } - - // Session Replay has its own queue - let targetQueue = isSnapshotEvent ? replayQueue : queue - - targetQueue?.add(posthogEvent) - - // Automatically set person properties for feature flags during capture event - setPersonPropertiesForFlagsIfNeeded(userProperties, userPropertiesSetOnce: userPropertiesSetOnce) - - #if os(iOS) - surveysIntegration?.onEvent(event: posthogEvent.event) - #endif - } - - @objc public func screen(_ screenTitle: String) { - screen(screenTitle, properties: nil) - } - - @objc(screenWithTitle:properties:) - public func screen(_ screenTitle: String, properties: [String: Any]? = nil) { - if !isEnabled() { - return - } - - if isOptOutState() { - return - } - - guard let queue else { - return - } - - let props = [ - "$screen_name": screenTitle, - ].merging(sanitizeDictionary(properties) ?? [:]) { prop, _ in prop } - - let distinctId = getDistinctId() - - let properties = buildProperties(distinctId: distinctId, properties: props) - - guard let event = buildEvent(event: "$screen", distinctId: distinctId, properties: properties) else { - return - } - - queue.add(event) - } - - func autocapture( - eventType: String, - elementsChain: String, - properties: [String: Any] - ) { - if !isEnabled() { - return - } - - if isOptOutState() { - return - } - - guard let queue else { - return - } - - let props = [ - "$event_type": eventType, - "$elements_chain": elementsChain, - ].merging(sanitizeDictionary(properties) ?? [:]) { prop, _ in prop } - - let distinctId = getDistinctId() - - let properties = buildProperties(distinctId: distinctId, properties: props) - - guard let event = buildEvent(event: "$autocapture", distinctId: distinctId, properties: properties) else { - return - } - - queue.add(event) - } - - private func sanitizeProperties(_ properties: [String: Any]) -> [String: Any] { - if let sanitizer = config.propertiesSanitizer { - return sanitizer.sanitize(properties) - } - return properties - } - - @objc public func alias(_ alias: String) { - if !isEnabled() { - return - } - - if isOptOutState() { - return - } - - if !requirePersonProcessing() { - return - } - - guard let queue else { - return - } - - let props = ["alias": alias] - - let distinctId = getDistinctId() - - let properties = buildProperties(distinctId: distinctId, properties: props) - - guard let event = buildEvent(event: "$create_alias", distinctId: distinctId, properties: properties) else { - return - } - - queue.add(event) - } - - private func groups(_ newGroups: [String: String]) -> [String: String] { - guard let storage else { - return [:] - } - - var groups: [String: String]? - var mergedGroups: [String: String]? - groupsLock.withLock { - groups = getGroups() - mergedGroups = groups?.merging(newGroups) { _, new in new } - - storage.setDictionary(forKey: .groups, contents: mergedGroups ?? [:]) - } - - var shouldReloadFlags = false - - for (key, value) in newGroups where groups?[key] != value { - shouldReloadFlags = true - break - } - - if shouldReloadFlags { - remoteConfig?.reloadFeatureFlags() - } - - return mergedGroups ?? [:] - } - - private func groupIdentify(type: String, key: String, groupProperties: [String: Any]? = nil) { - if !isEnabled() { - return - } - - if isOptOutState() { - return - } - - guard let queue else { - return - } - - var props: [String: Any] = ["$group_type": type, - "$group_key": key] - - let groupProps = sanitizeDictionary(groupProperties) - - if groupProps != nil { - props["$group_set"] = groupProps - } - - // Automatically set group properties for feature flags - setGroupPropertiesForFlagsIfNeeded(type: type, groupProperties: groupProperties) - - // Same as .group but without associating the current user with the group - let distinctId = getDistinctId() - - let properties = buildProperties(distinctId: distinctId, properties: props) - - guard let event = buildEvent(event: "$groupidentify", distinctId: distinctId, properties: properties) else { - return - } - - queue.add(event) - } - - func buildEvent(event eventName: String, distinctId: String, properties: [String: Any], timestamp: Date = Date()) -> PostHogEvent? { - let sanitizedProperties = sanitizeProperties(properties) - - let event = PostHogEvent( - event: eventName, - distinctId: distinctId, - properties: sanitizedProperties, - timestamp: timestamp - ) - - let resultEvent = config.runBeforeSend(event) - - if resultEvent == nil { - let originalMessage = "PostHog event \(eventName) was dropped" - let message = PostHogKnownUnsafeEditableEvent.contains(eventName) - ? "\(originalMessage). This can cause unexpected behavior." - : originalMessage - - hedgeLog(message) - } - - return resultEvent - } - - @objc(groupWithType:key:) - public func group(type: String, key: String) { - group(type: type, key: key, groupProperties: nil) - } - - @objc(groupWithType:key:groupProperties:) - public func group(type: String, key: String, groupProperties: [String: Any]? = nil) { - if !isEnabled() { - return - } - - if isOptOutState() { - return - } - - if !requirePersonProcessing() { - return - } - - _ = groups([type: key]) - - groupIdentify(type: type, key: key, groupProperties: sanitizeDictionary(groupProperties)) - } - - // FEATURE FLAGS - - /// Sets person properties that will be included in feature flag evaluation requests. - /// - /// This method allows you to override server-side person properties for immediate feature flag evaluation, - /// solving the race condition where person properties from `identify()` calls may not have been processed - /// by the server yet. - /// - /// Properties are merged additively with existing properties. Feature flags are automatically reloaded - /// after setting properties. - /// - /// ## Example Usage - /// ```swift - /// // Set properties and automatically reload flags - /// PostHogSDK.shared.setPersonPropertiesForFlags([ - /// "$app_version": "2.93.0", - /// "plan": "premium" - /// ]) - /// - /// // Now feature flags will be evaluated with these properties - /// let flagValue = PostHogSDK.shared.isFeatureEnabled("new_feature") - /// ``` - /// - /// - Parameter properties: Dictionary of person properties to include in flag evaluation - /// - SeeAlso: `setPersonPropertiesForFlags(_:reloadFeatureFlags:)` to control flag reloading behavior - @objc public func setPersonPropertiesForFlags(_ properties: [String: Any]) { - setPersonPropertiesForFlags(properties, reloadFeatureFlags: true) - } - - /// Sets person properties that will be included in feature flag evaluation requests. - /// - /// This method allows you to override server-side person properties for immediate feature flag evaluation, - /// solving the race condition where person properties from `identify()` calls may not have been processed - /// by the server yet. - /// - /// Properties are merged additively with existing properties. - /// - /// ## Example Usage - /// ```swift - /// // Set properties without automatically reloading flags - /// PostHogSDK.shared.setPersonPropertiesForFlags([ - /// "$app_version": "2.93.0", - /// "plan": "premium" - /// ], reloadFeatureFlags: false) - /// - /// // Manually reload flags later - /// PostHogSDK.shared.reloadFeatureFlags() - /// ``` - /// - /// - Parameters: - /// - properties: Dictionary of person properties to include in flag evaluation - /// - reloadFeatureFlags: Whether to automatically reload feature flags after setting properties - @objc(setPersonPropertiesForFlagsWithProperties:reloadFeatureFlags:) - public func setPersonPropertiesForFlags(_ properties: [String: Any], reloadFeatureFlags: Bool = true) { - if !isEnabled() { - return - } - - guard hasPersonProcessing() else { - return - } - - let sanitizedProperties = sanitizeDictionary(properties) ?? [:] - guard !sanitizedProperties.isEmpty else { return } - remoteConfig?.setPersonPropertiesForFlags(sanitizedProperties) - - if reloadFeatureFlags { - remoteConfig?.reloadFeatureFlags() - } - } - - /// Resets all person properties that were set for feature flag evaluation. - /// - /// After calling this method, feature flag evaluation will only use server-side person properties - /// and will not include any locally overridden properties. - /// - /// ## Example Usage - /// ```swift - /// // Clear all locally set person properties for flags - /// PostHogSDK.shared.resetPersonPropertiesForFlags() - /// - /// // Feature flags will now use only server-side properties - /// let flagValue = PostHogSDK.shared.isFeatureEnabled("feature") - /// ``` - /// - /// - Note: This method does not automatically reload feature flags. Call `reloadFeatureFlags()` - /// after resetting if you want to immediately refresh flags with the cleared properties. - @objc public func resetPersonPropertiesForFlags() { - if !isEnabled() { - return - } - - guard hasPersonProcessing() else { - return - } - - remoteConfig?.resetPersonPropertiesForFlags() - } - - /// Sets properties for a specific group type to include when evaluating feature flags. - /// These properties supplement the standard group information sent to PostHog for flag evaluation, - /// providing additional context that can be used in flag targeting conditions. - /// - /// ## Example Usage - /// ```swift - /// PostHogSDK.shared.setGroupPropertiesForFlags("organization", properties: [ - /// "plan": "enterprise", - /// "seats": 50, - /// "industry": "technology" - /// ]) - /// ``` - /// - /// - Parameters: - /// - groupType: The group type identifier (e.g., "organization", "team") - /// - properties: Dictionary of properties to set for this group type - /// - Note: This method automatically reloads feature flags to apply the new properties. - /// - SeeAlso: `setGroupPropertiesForFlags(_:properties:reloadFeatureFlags:)` to control flag reloading behavior - @objc(setGroupPropertiesForFlags:properties:) - public func setGroupPropertiesForFlags(_ groupType: String, properties: [String: Any]) { - setGroupPropertiesForFlags(groupType, properties: properties, reloadFeatureFlags: true) - } - - /// Sets properties for a specific group type to include when evaluating feature flags. - /// These properties supplement the standard group information sent to PostHog for flag evaluation, - /// providing additional context that can be used in flag targeting conditions. - /// - /// ## Example Usage - /// ```swift - /// // Set properties without automatically reloading flags - /// PostHogSDK.shared.setGroupPropertiesForFlags("organization", properties: [ - /// "plan": "enterprise", - /// "seats": 50 - /// ], reloadFeatureFlags: false) - /// - /// // Manually reload flags later - /// PostHogSDK.shared.reloadFeatureFlags() - /// ``` - /// - /// - Parameters: - /// - groupType: The group type identifier (e.g., "organization", "team") - /// - properties: Dictionary of properties to set for this group type - /// - reloadFeatureFlags: Whether to automatically reload feature flags after setting properties - @objc(setGroupPropertiesForFlags:properties:reloadFeatureFlags:) - public func setGroupPropertiesForFlags(_ groupType: String, properties: [String: Any], reloadFeatureFlags: Bool) { - if !isEnabled() { - return - } - - guard hasPersonProcessing() else { - return - } - - let sanitizedProperties = sanitizeDictionary(properties) ?? [:] - guard !sanitizedProperties.isEmpty else { return } - remoteConfig?.setGroupPropertiesForFlags(groupType, properties: sanitizedProperties) - - if reloadFeatureFlags { - remoteConfig?.reloadFeatureFlags() - } - } - - /// Clears all group properties for feature flag evaluation. - /// - /// ## Example Usage - /// ```swift - /// // Clear all group properties - /// PostHogSDK.shared.resetGroupPropertiesForFlags() - /// ``` - /// - /// - Note: This method does not automatically reload feature flags. Call `reloadFeatureFlags()` - /// after resetting if you want to immediately refresh flags with the cleared properties. - @objc public func resetGroupPropertiesForFlags() { - resetGroupPropertiesForFlags(groupType: nil) - } - - /// Clears group properties for feature flag evaluation for a specific group type. - /// - /// ## Example Usage - /// ```swift - /// // Clear properties for specific group type - /// PostHogSDK.shared.resetGroupPropertiesForFlags("organization") - /// ``` - /// - /// - Parameter groupType: The group type to clear properties for - /// - Note: This method does not automatically reload feature flags. Call `reloadFeatureFlags()` - /// after resetting if you want to immediately refresh flags with the cleared properties. - @objc(resetGroupPropertiesForFlagsWithGroupType:) - public func resetGroupPropertiesForFlags(_ groupType: String) { - resetGroupPropertiesForFlags(groupType: groupType) - } - - /// Internal implementation for resetting group properties. - private func resetGroupPropertiesForFlags(groupType: String?) { - if !isEnabled() { - return - } - - guard hasPersonProcessing() else { - return - } - - remoteConfig?.resetGroupPropertiesForFlags(groupType) - } - - @objc public func reloadFeatureFlags() { - reloadFeatureFlags { - // No use case - } - } - - @objc(reloadFeatureFlagsWithCallback:) - public func reloadFeatureFlags(_ callback: @escaping () -> Void) { - if !isEnabled() { - return - } - - remoteConfig?.reloadFeatureFlags { _ in - self.flagCallReportedLock.withLock { - self.flagCallReported.removeAll() - } - callback() - } - } - - @objc public func getFeatureFlag(_ key: String) -> Any? { - if !isEnabled() { - return nil - } - - guard let remoteConfig else { - return nil - } - - let value = remoteConfig.getFeatureFlag(key) - - if config.sendFeatureFlagEvent { - reportFeatureFlagCalled(flagKey: key, flagValue: value) - } - - return value - } - - @objc public func isFeatureEnabled(_ key: String) -> Bool { - let result = getFeatureFlag(key) - return result is String ? true : (result as? Bool) ?? false - } - - @objc public func getFeatureFlagPayload(_ key: String) -> Any? { - if !isEnabled() { - return nil - } - - guard let remoteConfig else { - return nil - } - - return remoteConfig.getFeatureFlagPayload(key) - } - - private func reportFeatureFlagCalled(flagKey: String, flagValue: Any?) { - var shouldCapture = false - - flagCallReportedLock.withLock { - if !flagCallReported.contains(flagKey) { - flagCallReported.insert(flagKey) - shouldCapture = true - } - } - - if shouldCapture { - let requestId = remoteConfig?.lastRequestId ?? "" - let details = remoteConfig?.getFeatureFlagDetails(flagKey) - - var properties = [ - "$feature_flag": flagKey, - "$feature_flag_response": flagValue ?? NSNull(), - "$feature_flag_request_id": requestId, - ] - - if let details = details as? [String: Any] { - if let reason = details["reason"] as? [String: Any] { - properties["$feature_flag_reason"] = reason["description"] ?? NSNull() - } - - if let metadata = details["metadata"] as? [String: Any] { - properties["$feature_flag_id"] = metadata["id"] ?? NSNull() - properties["$feature_flag_version"] = metadata["version"] ?? NSNull() - } - } - - capture("$feature_flag_called", properties: properties) - } - } - - private func isEnabled() -> Bool { - if !enabled { - hedgeLog("PostHog method was called without `setup` being complete. Call wil be ignored.") - } - return enabled - } - - @objc public func optIn() { - if !isEnabled() { - return - } - - if !isOptOut() { - return - } - - optOutLock.withLock { - config.optOut = false - storage?.setBool(forKey: .optOut, contents: false) - } - - setupLock.withLock { - installIntegrations() - } - } - - @objc public func optOut() { - if !isEnabled() { - return - } - - if isOptOut() { - return - } - - optOutLock.withLock { - config.optOut = true - storage?.setBool(forKey: .optOut, contents: true) - } - - setupLock.withLock { - uninstallIntegrations() - } - } - - @objc public func isOptOut() -> Bool { - if !isEnabled() { - return true - } - - return config.optOut - } - - @objc public func close() { - if !isEnabled() { - return - } - - setupLock.withLock { - enabled = false - PostHogSDK.apiKeys.remove(config.apiKey) - - queue?.stop() - replayQueue?.stop() - - queue = nil - replayQueue = nil - config.storageManager?.reset(keepAnonymousId: config.reuseAnonymousId) - config.storageManager = nil - config = PostHogConfig(apiKey: "") - remoteConfig = nil - storage = nil - #if !os(watchOS) - self.reachability?.stopNotifier() - reachability = nil - #endif - flagCallReportedLock.withLock { - flagCallReported.removeAll() - } - context = nil - sessionManager.endSession() - toggleHedgeLog(false) - - uninstallIntegrations() - } - } - - #if os(iOS) - /** - Starts session recording. - This method will have no effect if PostHog is not enabled, or if session replay is disabled in your project settings - - ## Note: - - Calling this method will resume the current session or create a new one if it doesn't exist - */ - @objc(startSessionRecording) - public func startSessionRecording() { - startSessionRecording(resumeCurrent: true) - } - - /** - Starts session recording. - This method will have no effect if PostHog is not enabled, or if session replay is disabled in your project settings - - - Parameter resumeCurrent: - Whether to resume recording of current session (true) or start a new session (false). - */ - @objc(startSessionRecordingWithResumeCurrent:) - public func startSessionRecording(resumeCurrent: Bool) { - if !isEnabled() { - return - } - - if isOptOutState() { - return - } - - if replayIntegration == nil { - // replay integration was not previously installed (probably disabled in config), attempt to install it - setupLock.withLock { - installReplayIntegration() - } - } - - guard let replayIntegration else { - return - } - - if resumeCurrent, replayIntegration.isActive() { - // nothing to resume, already active - return - } - - guard let remoteConfig, remoteConfig.isSessionReplayFlagActive() else { - return hedgeLog("Could not start recording. Session replay feature flag is disabled.") - } - - let sessionId = resumeCurrent - ? sessionManager.getSessionId() - : sessionManager.getNextSessionId() - - guard let sessionId else { - return hedgeLog("Could not start recording. Missing session id.") - } - - replayIntegration.start() - hedgeLog("Session replay recording started. Session id is \(sessionId)") - } - - /** - Stops the current session recording if one is in progress. - - This method will have no effect if PostHog is not enabled - */ - @objc public func stopSessionRecording() { - if !isEnabled() { - return - } - - guard let replayIntegration, replayIntegration.isActive() else { - return - } - - replayIntegration.stop() - hedgeLog("Session replay recording stopped.") - } - #endif - - @objc public static func with(_ config: PostHogConfig) -> PostHogSDK { - let postHog = PostHogSDK(config) - postHog.setup(config) - return postHog - } - - #if os(iOS) - @objc public func isSessionReplayActive() -> Bool { - if !isEnabled() { - return false - } - - guard let replayIntegration, let remoteConfig else { - return false - } - - return replayIntegration.isActive() - && !sessionManager.getSessionId(readOnly: true).isNilOrEmpty - && remoteConfig.isSessionReplayFlagActive() - } - #endif - - #if os(iOS) || targetEnvironment(macCatalyst) - @objc public func isAutocaptureActive() -> Bool { - isEnabled() && config.captureElementInteractions - } - #endif - - private func installIntegrations() { - guard installedIntegrations.isEmpty else { - hedgeLog("Integrations already installed. Call uninstallIntegrations() first.") - return - } - - let integrations = config.getIntegrations() - var installed: [PostHogIntegration] = [] - - for integration in integrations { - // Skip integrations that require swizzling when swizzling is disabled - if integration.requiresSwizzling, !config.enableSwizzling { - hedgeLog("Integration \(type(of: integration)) skipped. Integration requires swizzling but enableSwizzling is disabled in config") - continue - } - - do { - try integration.install(self) - installed.append(integration) - - #if os(iOS) - // TODO: Decouple these two integrations from PostHogSDK intance - if let replayIntegration = integration as? PostHogReplayIntegration { - self.replayIntegration = replayIntegration - } - - if let surveysIntegration = integration as? PostHogSurveyIntegration { - self.surveysIntegration = surveysIntegration - } - #endif - - hedgeLog("Integration \(type(of: integration)) installed") - } catch { - hedgeLog("Integration \(type(of: integration)) failed to install: \(error)") - } - } - - installedIntegrations = installed - } - - #if os(iOS) - private func installReplayIntegration() { - guard replayIntegration == nil else { return } - - let integration = PostHogReplayIntegration() - do { - try integration.install(self) - installedIntegrations.append(integration) - replayIntegration = integration - - hedgeLog("Integration \(type(of: integration)) installed") - } catch { - hedgeLog("Integration \(type(of: integration)) failed to install: \(error)") - } - } - #endif - - private func uninstallIntegrations() { - for integration in installedIntegrations { - integration.uninstall(self) - hedgeLog("Integration \(type(of: integration)) uninstalled") - } - installedIntegrations = [] - - #if os(iOS) - replayIntegration = nil - surveysIntegration = nil - #endif - } -} - -#if TESTING - extension PostHogSDK { - #if os(iOS) || targetEnvironment(macCatalyst) - func getAutocaptureIntegration() -> PostHogAutocaptureIntegration? { - installedIntegrations.compactMap { - $0 as? PostHogAutocaptureIntegration - }.first - } - #endif - - #if os(iOS) - func getReplayIntegration() -> PostHogReplayIntegration? { - installedIntegrations.compactMap { - $0 as? PostHogReplayIntegration - }.first - } - #endif - - func getSessionManager() -> PostHogSessionManager? { - sessionManager - } - - func getAppLifeCycleIntegration() -> PostHogAppLifeCycleIntegration? { - installedIntegrations.compactMap { - $0 as? PostHogAppLifeCycleIntegration - }.first - } - - func getScreenViewIntegration() -> PostHogScreenViewIntegration? { - installedIntegrations.compactMap { - $0 as? PostHogScreenViewIntegration - }.first - } - } -#endif - -// swiftlint:enable file_length cyclomatic_complexity diff --git a/Pods/PostHog/PostHog/PostHogSessionManager.swift b/Pods/PostHog/PostHog/PostHogSessionManager.swift deleted file mode 100644 index 07de4bc..0000000 --- a/Pods/PostHog/PostHog/PostHogSessionManager.swift +++ /dev/null @@ -1,271 +0,0 @@ -// -// PostHogSessionManager.swift -// PostHog -// -// Created by Manoel Aranda Neto on 28.08.24. -// - -import Foundation - -// only for internal use -// Do we need to expose this as public API? Could be internal static instead? -@objc public class PostHogSessionManager: NSObject { - enum SessionIDChangeReason: String { - case sessionIdEmpty = "Session id was empty" - case sessionStart = "Session started" - case sessionEnd = "Session ended" - case sessionReset = "Session was reset" - case sessionTimeout = "Session timed out" - case sessionPastMaximumLength = "Session past maximum length" - case customSessionId = "Custom session set" - } - - @objc public static var shared: PostHogSessionManager { - PostHogSDK.shared.sessionManager - } - - private var config: PostHogConfig? - - override init() { - super.init() - } - - func setup(config: PostHogConfig) { - self.config = config - didBecomeActiveToken = nil - didEnterBackgroundToken = nil - applicationEventToken = nil - registerNotifications() - registerApplicationSendEvent() - } - - func reset() { - resetSession() - didBecomeActiveToken = nil - didEnterBackgroundToken = nil - applicationEventToken = nil - } - - private let queue = DispatchQueue(label: "com.posthog.PostHogSessionManager", target: .global(qos: .utility)) - private var sessionId: String? - private var sessionStartTimestamp: TimeInterval? - private var sessionActivityTimestamp: TimeInterval? - private let sessionLock = NSLock() - private var isAppInBackground = true - // 30 minutes in seconds - private let sessionActivityThreshold: TimeInterval = 60 * 30 - // 24 hours in seconds - private let sessionMaxLengthThreshold: TimeInterval = 24 * 60 * 60 - // Called when session id is cleared or changes - var onSessionIdChanged: () -> Void = {} - - @objc public func setSessionId(_ sessionId: String) { - setSessionIdInternal(sessionId, at: now(), reason: .customSessionId) - } - - private func isNotReactNative() -> Bool { - // for the RN SDK, the session is handled by the RN SDK itself - postHogSdkName != "posthog-react-native" - } - - /** - Returns the current session id, and manages id rotation logic - - In addition, this method handles core session cycling logic including: - - Creates a new session id when none exists (but only if app is foregrounded) - - if `readOnly` is false - - Rotates session after *30 minutes* of inactivity - - Clears session after *30 minutes* of inactivity (when app is backgrounded) - - Enforces a maximum session duration of *24 hours* - - - Parameters: - - timeNow: Reference timestamp used for evaluating session expiry rules. - Defaults to current system time. - - readOnly: When true, bypasses all session management logic and returns - the current session id without modifications. - Defaults to false. - - - Returns: Returns the existing session id, or a new one after performing validity checks - */ - func getSessionId( - at timeNow: Date = now(), - readOnly: Bool = false - ) -> String? { - let timestamp = timeNow.timeIntervalSince1970 - let (currentSessionId, lastActive, sessionStart, isBackgrounded) = sessionLock.withLock { - (sessionId, sessionActivityTimestamp, sessionStartTimestamp, isAppInBackground) - } - - // RN manages its own session, just return session id - guard isNotReactNative(), !readOnly else { - return currentSessionId - } - - // Create a new session id if empty - if currentSessionId.isNilOrEmpty, !isBackgrounded { - return rotateSession(force: true, at: timeNow, reason: .sessionIdEmpty) - } - - // Check if session has passed maximum inactivity length - if let lastActive, isExpired(timestamp, lastActive, sessionActivityThreshold) { - return isBackgrounded - ? clearSession(reason: .sessionTimeout) - : rotateSession(at: timeNow, reason: .sessionTimeout) - } - - // Check if session has passed maximum session length - if let sessionStart, isExpired(timestamp, sessionStart, sessionMaxLengthThreshold) { - return isBackgrounded - ? clearSession(reason: .sessionPastMaximumLength) - : rotateSession(at: timeNow, reason: .sessionPastMaximumLength) - } - - return currentSessionId - } - - func getNextSessionId() -> String? { - // if this is RN, return the current session id - guard isNotReactNative() else { - return sessionLock.withLock { sessionId } - } - - return rotateSession(force: true, at: now(), reason: .sessionStart) - } - - /// Creates a new session id and sets timestamps - func startSession(_ completion: (() -> Void)? = nil) { - guard isNotReactNative() else { return } - - rotateSession(force: true, at: now(), reason: .sessionStart) - completion?() - } - - /// Clears current session id and timestamps - func endSession(_ completion: (() -> Void)? = nil) { - guard isNotReactNative() else { return } - - clearSession(reason: .sessionEnd) - completion?() - } - - /// Resets current session id and timestamps - func resetSession() { - guard isNotReactNative() else { return } - - rotateSession(force: true, at: now(), reason: .sessionReset) - } - - /// Call this method to mark any user activity on this session - func touchSession() { - guard isNotReactNative() else { return } - - let (currentSessionId, lastActive) = sessionLock.withLock { - (sessionId, sessionActivityTimestamp) - } - - guard currentSessionId != nil else { return } - - let timeNow = now() - let timestamp = timeNow.timeIntervalSince1970 - - // Check if session has passed maximum inactivity length between user activity marks - if let lastActive, isExpired(timestamp, lastActive, sessionActivityThreshold) { - rotateSession(at: timeNow, reason: .sessionTimeout) - } else { - sessionLock.withLock { - sessionActivityTimestamp = timestamp - } - } - } - - /** - Rotates the current session id - - - Parameters: - - force: When true, creates a new session ID if current one is empty - - reason: The underlying reason behind this session ID rotation - - Returns: a new session id - */ - @discardableResult private func rotateSession(force: Bool = false, at timestamp: Date, reason: SessionIDChangeReason) -> String? { - // only rotate when session is empty - if !force { - let currentSessionId = sessionLock.withLock { sessionId } - if currentSessionId.isNilOrEmpty { - return currentSessionId - } - } - - let newSessionId = UUID.v7().uuidString - setSessionIdInternal(newSessionId, at: timestamp, reason: reason) - return newSessionId - } - - @discardableResult private func clearSession(reason: SessionIDChangeReason) -> String? { - setSessionIdInternal(nil, at: nil, reason: reason) - return nil - } - - private func setSessionIdInternal(_ sessionId: String?, at timestamp: Date?, reason: SessionIDChangeReason) { - let timestamp = timestamp?.timeIntervalSince1970 - - sessionLock.withLock { - self.sessionId = sessionId - self.sessionStartTimestamp = timestamp - self.sessionActivityTimestamp = timestamp - } - - onSessionIdChanged() - - if let sessionId { - hedgeLog("New session id created \(sessionId) (\(reason))") - } else { - hedgeLog("Session id cleared - reason: (\(reason))") - } - } - - private var didBecomeActiveToken: RegistrationToken? - private var didEnterBackgroundToken: RegistrationToken? - - private func registerNotifications() { - let lifecyclePublisher = DI.main.appLifecyclePublisher - didBecomeActiveToken = lifecyclePublisher.onDidBecomeActive { [weak self] in - guard let self, sessionLock.withLock({ self.isAppInBackground }) else { - return - } - - // we consider foregrounding an app an activity on the current session - touchSession() - sessionLock.withLock { self.isAppInBackground = false } - } - didEnterBackgroundToken = lifecyclePublisher.onDidEnterBackground { [weak self] in - guard let self, !sessionLock.withLock({ self.isAppInBackground }) else { - return - } - - // we consider backgrounding the app an activity on the current session - touchSession() - sessionLock.withLock { self.isAppInBackground = true } - } - } - - private var applicationEventToken: RegistrationToken? - - private func registerApplicationSendEvent() { - #if os(iOS) || os(tvOS) - guard let config, config.enableSwizzling else { - return - } - applicationEventToken = DI.main.applicationEventPublisher.onApplicationEvent { [weak self] _, _ in - // update "last active" session - // we want to keep track of the idle time, so we need to maintain a timestamp on the last interactions of the user with the app. UIEvents are a good place to do so since it means that the user is actively interacting with the app (e.g not just noise background activity) - self?.queue.async { - self?.touchSession() - } - } - #endif - } - - private func isExpired(_ timeNow: TimeInterval, _ timeThen: TimeInterval, _ threshold: TimeInterval) -> Bool { - max(timeNow - timeThen, 0) > threshold - } -} diff --git a/Pods/PostHog/PostHog/PostHogStorage.swift b/Pods/PostHog/PostHog/PostHogStorage.swift deleted file mode 100644 index 5bde9bd..0000000 --- a/Pods/PostHog/PostHog/PostHogStorage.swift +++ /dev/null @@ -1,419 +0,0 @@ -// -// PostHogStorage.swift -// PostHog -// -// Created by Ben White on 08.02.23. -// - -import Foundation - -/** - # Storage - - Note for tvOS: - As tvOS restricts access to persisted Application Support directory, we use Library/Caches instead for storage - - If needed, we can use UserDefaults for lightweight data - according to Apple, you can use UserDefaults to persist up to 500KB of data on tvOS - see: https://developer.apple.com/forums/thread/16967?answerId=50696022#50696022 - */ -func applicationSupportDirectoryURL() -> URL { - #if os(tvOS) - // tvOS restricts access to Application Support directory on physical devices - // Use Library/Caches directory which may have less frequent eviction behavior than temp (which is purged when the app quits) - let searchPath: FileManager.SearchPathDirectory = .cachesDirectory - #else - let searchPath: FileManager.SearchPathDirectory = .applicationSupportDirectory - #endif - - let url = FileManager.default.urls(for: searchPath, in: .userDomainMask).first! - let bundleIdentifier = getBundleIdentifier() - - return url.appendingPathComponent(bundleIdentifier) -} - -/** - - From Apple Docs: - In iOS, the value is nil when the group identifier is invalid. In macOS, a URL of the expected form is always - returned, even if the app group is invalid, so be sure to test that you can access the underlying directory - before attempting to use it. - - MacOS: The system also creates the Library/Application Support, Library/Caches, and Library/Preferences - subdirectories inside the group directory the first time you use it - iOS: The system creates only the Library/Caches subdirectory automatically - - see: https://developer.apple.com/documentation/foundation/filemanager/1412643-containerurl/ - */ -func appGroupContainerUrl(config: PostHogConfig) -> URL? { - guard let appGroupIdentifier = config.appGroupIdentifier else { return nil } - - #if os(tvOS) - // tvOS: Due to stricter sandbox rules, creating "Application Support" directory is not possible on tvOS - let librarySubPath = "Library/Caches/" - #else - let librarySubPath = "Library/Application Support/" - #endif - - let libraryUrl = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)? - .appendingPathComponent(librarySubPath) - - guard let url = libraryUrl?.appendingPathComponent(appGroupIdentifier) else { return nil } - - createDirectoryAtURLIfNeeded(url: url) - - // Merges a legacy container (using bundleIdentifier) into the new container using appGroupIdentifier - mergeLegacyContainerIfNeeded(within: libraryUrl, to: url) - - return directoryExists(url) ? url : nil -} - -func getBundleIdentifier() -> String { - #if TESTING // only visible to test targets - return Bundle.main.bundleIdentifier ?? "com.posthog.test" - #else - return Bundle.main.bundleIdentifier! - #endif -} - -/** - Merges content from a legacy container directory into the current app group container. - - This function handles the migration of PostHog data from the old storage location (using `bundleIdentifier`) - to the new app group shared container location (using `appGroupIdentifier`). - - Migration rules: - - Files that already exist at the destination are skipped (no overwrite) - - The anonymousId from the first processed container (legacy or current) is preserved to maintain user identity - - Successfully migrated files are deleted from the source - - Empty directories are cleaned up after migration - - The entire folder structure is preserved during migration - - - Parameters: - - libraryUrl: The base library URL where both legacy and new containers might exist - - destinationUrl: The target app group container URL where files should be migrated - */ -func mergeLegacyContainerIfNeeded(within libraryUrl: URL?, to destinationUrl: URL) { - let bundleIdentifier = getBundleIdentifier() - guard let sourceUrl = libraryUrl?.appendingPathComponent(bundleIdentifier), directoryExists(sourceUrl) else { - return - } - - hedgeLog("Legacy folder found at \(sourceUrl), merging...") - - // Migrate all contents from the legacy container - migrateDirectoryContents(from: sourceUrl, to: destinationUrl) - - // Try to remove the source directory if it's empty - if removeIfEmpty(sourceUrl) { - hedgeLog("Successfully migrated and removed legacy folder at \(sourceUrl)") - } -} - -/** - Removes a directory if it's empty. - - - Parameters: - - url: The directory URL to potentially remove - - Returns: `true` if the directory was removed, `false` otherwise - */ -@discardableResult -func removeIfEmpty(_ url: URL) -> Bool { - let remainingItems = try? FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: []) - if remainingItems?.isEmpty == true { - do { - try FileManager.default.removeItem(at: url) - return true - } catch { - hedgeLog("Failed to remove empty directory at \(url.path): \(error)") - } - } - return false -} - -/** - Migrates a single file from source to destination. - - Migration rules: - - If the file doesn't exist at destination, it's copied and then deleted from source - - If the file already exists at destination, only the source file is deleted - - - Parameters: - - sourceFile: The source file URL - - destinationFile: The destination file URL - - Throws: Any errors that occur during file operations - */ -func migrateFile(from sourceFile: URL, to destinationFile: URL) throws { - if !FileManager.default.fileExists(atPath: destinationFile.path) { - try FileManager.default.copyItem(at: sourceFile, to: destinationFile) - } - // Always delete source file after processing (whether copied or skipped) - try FileManager.default.removeItem(at: sourceFile) -} - -/** - Recursively migrates all contents from a source directory to a destination directory. - - - Parameters: - - sourceDir: The source directory URL - - destinationDir: The destination directory URL - */ -func migrateDirectoryContents(from sourceDir: URL, to destinationDir: URL) { - do { - // Create destination directory if it doesn't exist (we need to call this here again as the function is recursive) - createDirectoryAtURLIfNeeded(url: destinationDir) - - // Get all items in source directory - let items = try FileManager.default.contentsOfDirectory(at: sourceDir, includingPropertiesForKeys: nil, options: []) - - for item in items { - let destinationItem = destinationDir.appendingPathComponent(item.lastPathComponent) - - // Check if it's a directory - var isDirectory: ObjCBool = false - if FileManager.default.fileExists(atPath: item.path, isDirectory: &isDirectory) { - if isDirectory.boolValue { - // Recursively migrate subdirectory (preserving the folder structure) - migrateDirectoryContents(from: item, to: destinationItem) - // Remove empty directory after migration - removeIfEmpty(item) - } else { - // Migrate file - do { - try migrateFile(from: item, to: destinationItem) - } catch { - hedgeLog("Failed to migrate file from \(item.path) to \(destinationItem.path): \(error)") - } - } - } - } - } catch { - hedgeLog("Error reading directory contents at \(sourceDir.path): \(error)") - } -} - -class PostHogStorage { - // when adding or removing items here, make sure to update the reset method - enum StorageKey: String, CaseIterable { - case distinctId = "posthog.distinctId" - case anonymousId = "posthog.anonymousId" - case queue = "posthog.queueFolder" // NOTE: This is different to posthog-ios v2 - case oldQeueue = "posthog.queue.plist" - case replayQeueue = "posthog.replayFolder" - case enabledFeatureFlags = "posthog.enabledFeatureFlags" - case enabledFeatureFlagPayloads = "posthog.enabledFeatureFlagPayloads" - case flags = "posthog.flags" - case groups = "posthog.groups" - case registerProperties = "posthog.registerProperties" - case optOut = "posthog.optOut" - case sessionReplay = "posthog.sessionReplay" - case isIdentified = "posthog.isIdentified" - case personProcessingEnabled = "posthog.enabledPersonProcessing" - case remoteConfig = "posthog.remoteConfig" - case surveySeen = "posthog.surveySeen" - case requestId = "posthog.requestId" - case personPropertiesForFlags = "posthog.personPropertiesForFlags" - case groupPropertiesForFlags = "posthog.groupPropertiesForFlags" - } - - // The location for storing data that we always want to keep - let appFolderUrl: URL - - init(_ config: PostHogConfig) { - appFolderUrl = Self.getAppFolderUrl(from: config) - - // migrate legacy storage if needed - Self.migrateLegacyStorage(from: config, to: appFolderUrl) - } - - func url(forKey key: StorageKey) -> URL { - appFolderUrl.appendingPathComponent(key.rawValue) - } - - // The "data" methods are the core for storing data and differ between Modes - // All other typed storage methods call these - private func getData(forKey: StorageKey) -> Data? { - let url = url(forKey: forKey) - - do { - if FileManager.default.fileExists(atPath: url.path) { - return try Data(contentsOf: url) - } - } catch { - hedgeLog("Error reading data from key \(forKey): \(error)") - } - return nil - } - - private func setData(forKey: StorageKey, contents: Data?) { - var url = url(forKey: forKey) - - do { - if contents == nil { - deleteSafely(url) - return - } - - try contents?.write(to: url) - - var resourceValues = URLResourceValues() - resourceValues.isExcludedFromBackup = true - try url.setResourceValues(resourceValues) - } catch { - hedgeLog("Failed to write data for key '\(forKey)' error: \(error)") - } - } - - private func getJson(forKey key: StorageKey) -> Any? { - guard let data = getData(forKey: key) else { return nil } - - do { - return try JSONSerialization.jsonObject(with: data) - } catch { - hedgeLog("Failed to serialize key '\(key)' error: \(error)") - } - return nil - } - - private func setJson(forKey key: StorageKey, json: Any) { - var jsonObject: Any? - - if let dictionary = json as? [AnyHashable: Any] { - jsonObject = dictionary - } else if let array = json as? [Any] { - jsonObject = array - } else { - // TRICKY: This is weird legacy behaviour storing the data as a dictionary - jsonObject = [key.rawValue: json] - } - - var data: Data? - do { - data = try JSONSerialization.data(withJSONObject: jsonObject!) - } catch { - hedgeLog("Failed to serialize key '\(key)' error: \(error)") - } - setData(forKey: key, contents: data) - } - - /** - There are cases where applications using posthog-ios want to share analytics data between host app and - an app extension, Widget or App Clip. If there's a defined `appGroupIdentifier` in configuration, - we want to use a shared container for storing data so that extensions correctly identify a user (and batch process events) - */ - private static func getBaseAppFolderUrl(from configuration: PostHogConfig) -> URL { - appGroupContainerUrl(config: configuration) ?? applicationSupportDirectoryURL() - } - - private static func migrateItem(at sourceUrl: URL, to destinationUrl: URL, fileManager: FileManager) throws { - guard fileManager.fileExists(atPath: sourceUrl.path) else { return } - // Copy file or directory over (if it doesn't exist) - if !fileManager.fileExists(atPath: destinationUrl.path) { - try fileManager.copyItem(at: sourceUrl, to: destinationUrl) - } - } - - private static func migrateLegacyStorage(from configuration: PostHogConfig, to apiDir: URL) { - let legacyUrl = getBaseAppFolderUrl(from: configuration) - if directoryExists(legacyUrl) { - let fileManager = FileManager.default - - // Migrate old files that correspond to StorageKey values - for storageKey in StorageKey.allCases { - let legacyFileUrl = legacyUrl.appendingPathComponent(storageKey.rawValue) - let newFileUrl = apiDir.appendingPathComponent(storageKey.rawValue) - - do { - // Migrate the item and its contents if it exists - try migrateItem(at: legacyFileUrl, to: newFileUrl, fileManager: fileManager) - } catch { - hedgeLog("Error during storage migration for file \(storageKey.rawValue) at path \(legacyFileUrl.path): \(error)") - } - - // Remove the legacy item after successful migration - if fileManager.fileExists(atPath: legacyFileUrl.path) { - do { - try fileManager.removeItem(at: legacyFileUrl) - } catch { - hedgeLog("Could not delete file \(storageKey.rawValue) at path \(legacyFileUrl.path): \(error)") - } - } - } - } - } - - private static func getAppFolderUrl(from configuration: PostHogConfig) -> URL { - let apiDir = getBaseAppFolderUrl(from: configuration) - .appendingPathComponent(configuration.apiKey) - - createDirectoryAtURLIfNeeded(url: apiDir) - - return apiDir - } - - func reset(keepAnonymousId: Bool = false) { - // sadly the StorageKey.allCases does not work here - deleteSafely(url(forKey: .distinctId)) - if !keepAnonymousId { - deleteSafely(url(forKey: .anonymousId)) - } - // .queue, .replayQeueue not needed since it'll be deleted by the queue.clear() - deleteSafely(url(forKey: .oldQeueue)) - deleteSafely(url(forKey: .flags)) - deleteSafely(url(forKey: .enabledFeatureFlags)) - deleteSafely(url(forKey: .enabledFeatureFlagPayloads)) - deleteSafely(url(forKey: .groups)) - deleteSafely(url(forKey: .registerProperties)) - deleteSafely(url(forKey: .optOut)) - deleteSafely(url(forKey: .sessionReplay)) - deleteSafely(url(forKey: .isIdentified)) - deleteSafely(url(forKey: .personProcessingEnabled)) - deleteSafely(url(forKey: .remoteConfig)) - deleteSafely(url(forKey: .surveySeen)) - deleteSafely(url(forKey: .requestId)) - deleteSafely(url(forKey: .personPropertiesForFlags)) - deleteSafely(url(forKey: .groupPropertiesForFlags)) - } - - func remove(key: StorageKey) { - let url = url(forKey: key) - - deleteSafely(url) - } - - func getString(forKey key: StorageKey) -> String? { - let value = getJson(forKey: key) - if let stringValue = value as? String { - return stringValue - } else if let dictValue = value as? [String: String] { - return dictValue[key.rawValue] - } - return nil - } - - func setString(forKey key: StorageKey, contents: String) { - setJson(forKey: key, json: contents) - } - - func getDictionary(forKey key: StorageKey) -> [AnyHashable: Any]? { - getJson(forKey: key) as? [AnyHashable: Any] - } - - func setDictionary(forKey key: StorageKey, contents: [AnyHashable: Any]) { - setJson(forKey: key, json: contents) - } - - func getBool(forKey key: StorageKey) -> Bool? { - let value = getJson(forKey: key) - if let boolValue = value as? Bool { - return boolValue - } else if let dictValue = value as? [String: Bool] { - return dictValue[key.rawValue] - } - return nil - } - - func setBool(forKey key: StorageKey, contents: Bool) { - setJson(forKey: key, json: contents) - } -} diff --git a/Pods/PostHog/PostHog/PostHogStorageManager.swift b/Pods/PostHog/PostHog/PostHogStorageManager.swift deleted file mode 100644 index 685473e..0000000 --- a/Pods/PostHog/PostHog/PostHogStorageManager.swift +++ /dev/null @@ -1,163 +0,0 @@ -// -// PostHogStorageManager.swift -// PostHog -// -// Created by Ben White on 08.02.23. -// - -import Foundation - -// Internal class to manage the storage metadata of the PostHog SDK -public class PostHogStorageManager { - private let storage: PostHogStorage! - - private let anonLock = NSLock() - private let distinctLock = NSLock() - private let identifiedLock = NSLock() - private let personProcessingLock = NSLock() - private let idGen: (UUID) -> UUID - - private var distinctId: String? - private var cachedDistinctId = false - private var anonymousId: String? - private var isIdentifiedValue: Bool? - private var personProcessingEnabled: Bool? - - init(_ config: PostHogConfig) { - storage = PostHogStorage(config) - idGen = config.getAnonymousId - } - - public func getAnonymousId() -> String { - anonLock.withLock { - if anonymousId == nil { - var anonymousId = storage.getString(forKey: .anonymousId) - - if anonymousId == nil { - let uuid = UUID.v7() - anonymousId = idGen(uuid).uuidString - setAnonId(anonymousId ?? "") - } else { - // update the memory value - self.anonymousId = anonymousId - } - } - } - - return anonymousId ?? "" - } - - public func setAnonymousId(_ id: String) { - anonLock.withLock { - setAnonId(id) - } - } - - private func setAnonId(_ id: String) { - anonymousId = id - storage.setString(forKey: .anonymousId, contents: id) - } - - public func getDistinctId() -> String { - var distinctId: String? - distinctLock.withLock { - if self.distinctId == nil { - // since distinctId is nil until its identified, no need to read from - // cache every single time, otherwise anon users will never used the - // cached values - if !cachedDistinctId { - distinctId = storage.getString(forKey: .distinctId) - cachedDistinctId = true - } - - // do this to not assign the AnonymousId to the DistinctId, its just a fallback - if distinctId == nil { - distinctId = getAnonymousId() - } else { - // update the memory value - self.distinctId = distinctId - } - } else { - // read from memory - distinctId = self.distinctId - } - } - return distinctId ?? "" - } - - public func setDistinctId(_ id: String) { - distinctLock.withLock { - distinctId = id - storage.setString(forKey: .distinctId, contents: id) - } - } - - public func isIdentified() -> Bool { - identifiedLock.withLock { - if isIdentifiedValue == nil { - isIdentifiedValue = storage.getBool(forKey: .isIdentified) ?? (getDistinctId() != getAnonymousId()) - } - } - return isIdentifiedValue ?? false - } - - public func setIdentified(_ isIdentified: Bool) { - identifiedLock.withLock { - isIdentifiedValue = isIdentified - storage.setBool(forKey: .isIdentified, contents: isIdentified) - } - } - - public func isPersonProcessing() -> Bool { - personProcessingLock.withLock { - if personProcessingEnabled == nil { - personProcessingEnabled = storage.getBool(forKey: .personProcessingEnabled) ?? false - } - } - return personProcessingEnabled ?? false - } - - public func setPersonProcessing(_ enable: Bool) { - personProcessingLock.withLock { - // only set if its different to avoid IO since this is called more often - if self.personProcessingEnabled != enable { - self.personProcessingEnabled = enable - storage.setBool(forKey: .personProcessingEnabled, contents: enable) - } - } - } - - public func reset(keepAnonymousId: Bool = false, _ resetStorage: Bool = false) { - // resetStorage is only used for testing, when the reset method is called, - // the storage is also cleared, so we don't do here to not do it twice. - distinctLock.withLock { - distinctId = nil - cachedDistinctId = false - if resetStorage { - storage.remove(key: .distinctId) - } - } - - if !keepAnonymousId { - anonLock.withLock { - anonymousId = nil - if resetStorage { - storage.remove(key: .anonymousId) - } - } - } - - identifiedLock.withLock { - isIdentifiedValue = nil - if resetStorage { - storage.remove(key: .isIdentified) - } - } - personProcessingLock.withLock { - personProcessingEnabled = nil - if resetStorage { - storage.remove(key: .personProcessingEnabled) - } - } - } -} diff --git a/Pods/PostHog/PostHog/PostHogSwizzler.swift b/Pods/PostHog/PostHog/PostHogSwizzler.swift deleted file mode 100644 index 7aaf73c..0000000 --- a/Pods/PostHog/PostHog/PostHogSwizzler.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// PostHogSwizzler.swift -// PostHog -// -// Created by Manoel Aranda Neto on 26.03.24. -// - -import Foundation - -func swizzle(forClass: AnyClass, original: Selector, new: Selector) { - guard let originalMethod = class_getInstanceMethod(forClass, original) else { return } - guard let swizzledMethod = class_getInstanceMethod(forClass, new) else { return } - method_exchangeImplementations(originalMethod, swizzledMethod) -} diff --git a/Pods/PostHog/PostHog/PostHogVersion.swift b/Pods/PostHog/PostHog/PostHogVersion.swift deleted file mode 100644 index 8943742..0000000 --- a/Pods/PostHog/PostHog/PostHogVersion.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// PostHogVersion.swift -// PostHog -// -// Created by Manoel Aranda Neto on 13.10.23. -// - -import Foundation - -// if you change this, make sure to also change it in the podspec and check if the script scripts/bump-version.sh still works -// This property is internal only -public var postHogVersion = "3.34.0" - -public let postHogiOSSdkName = "posthog-ios" -// This property is internal only -public var postHogSdkName = postHogiOSSdkName diff --git a/Pods/PostHog/PostHog/Replay/ApplicationEventPublisher.swift b/Pods/PostHog/PostHog/Replay/ApplicationEventPublisher.swift deleted file mode 100644 index 98c5782..0000000 --- a/Pods/PostHog/PostHog/Replay/ApplicationEventPublisher.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// ApplicationEventPublisher.swift -// PostHog -// -// Created by Ioannis Josephides on 24/02/2025. -// - -import Foundation - -#if os(iOS) || os(tvOS) - import UIKit - - typealias ApplicationEventHandler = (_ event: UIEvent, _ date: Date) -> Void - - protocol ApplicationEventPublishing: AnyObject { - /// Registers a callback for a `UIApplication.sendEvent` - func onApplicationEvent(_ callback: @escaping ApplicationEventHandler) -> RegistrationToken - } - - final class ApplicationEventPublisher: BaseApplicationEventPublisher { - static let shared = ApplicationEventPublisher() - - private var hasSwizzled: Bool = false - - func start() { - swizzleSendEvent() - } - - func stop() { - unswizzleSendEvent() - } - - func swizzleSendEvent() { - guard !hasSwizzled else { return } - hasSwizzled = true - - swizzle( - forClass: UIApplication.self, - original: #selector(UIApplication.sendEvent(_:)), - new: #selector(UIApplication.sendEventOverride) - ) - } - - func unswizzleSendEvent() { - guard hasSwizzled else { return } - hasSwizzled = false - - // swizzling twice will exchange implementations back to original - swizzle( - forClass: UIApplication.self, - original: #selector(UIApplication.sendEvent(_:)), - new: #selector(UIApplication.sendEventOverride) - ) - } - - override func onApplicationEvent(_ callback: @escaping ApplicationEventHandler) -> RegistrationToken { - let id = UUID() - registrationLock.withLock { - self.onApplicationEventCallbacks[id] = callback - } - - // start on first callback registration - if !hasSwizzled { - start() - } - - return RegistrationToken { [weak self] in - // Registration token deallocated here - guard let self else { return } - let handlerCount = self.registrationLock.withLock { - self.onApplicationEventCallbacks[id] = nil - return self.onApplicationEventCallbacks.values.count - } - - // stop when there are no more callbacks - if handlerCount <= 0 { - self.stop() - } - } - } - - // Called from swizzled `UIApplication.sendEvent` - fileprivate func sendEvent(event: UIEvent, date: Date) { - notifyHandlers(uiEvent: event, date: date) - } - } - - class BaseApplicationEventPublisher: ApplicationEventPublishing { - fileprivate let registrationLock = NSLock() - - var onApplicationEventCallbacks: [UUID: ApplicationEventHandler] = [:] - - func onApplicationEvent(_ callback: @escaping ApplicationEventHandler) -> RegistrationToken { - let id = UUID() - registrationLock.withLock { - self.onApplicationEventCallbacks[id] = callback - } - - return RegistrationToken { [weak self] in - // Registration token deallocated here - guard let self else { return } - self.registrationLock.withLock { - self.onApplicationEventCallbacks[id] = nil - } - } - } - - func notifyHandlers(uiEvent: UIEvent, date: Date) { - let handlers = registrationLock.withLock { onApplicationEventCallbacks.values } - for handler in handlers { - notifyHander(handler, uiEvent: uiEvent, date: date) - } - } - - private func notifyHander(_ handler: @escaping ApplicationEventHandler, uiEvent: UIEvent, date: Date) { - if Thread.isMainThread { - handler(uiEvent, date) - } else { - DispatchQueue.main.async { handler(uiEvent, date) } - } - } - } - - extension UIApplication { - @objc func sendEventOverride(_ event: UIEvent) { - sendEventOverride(event) - ApplicationEventPublisher.shared.sendEvent(event: event, date: Date()) - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/CGColor+Util.swift b/Pods/PostHog/PostHog/Replay/CGColor+Util.swift deleted file mode 100644 index 9fee8b5..0000000 --- a/Pods/PostHog/PostHog/Replay/CGColor+Util.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// CGColor+Util.swift -// PostHog -// -// Created by Manoel Aranda Neto on 21.03.24. -// - -#if os(iOS) - - import Foundation - import UIKit - - extension CGColor { - func toRGBString() -> String? { - // see dicussion: https://github.com/PostHog/posthog-ios/issues/226 - // Allow only CGColors with an intiialized value of `numberOfComponents` with a value in 3...4 range - // Loading dynamic colors from storyboard sometimes leads to some random values for numberOfComponents like `105553118884896` which crashes the app - guard - 3 ... 4 ~= numberOfComponents, // check range - let components = components, // we now assume it's safe to access `components` - components.count >= 3 - else { - return nil - } - - let red = Int(components[0] * 255) - let green = Int(components[1] * 255) - let blue = Int(components[2] * 255) - - return String(format: "#%02X%02X%02X", red, green, blue) - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/CGSize+Util.swift b/Pods/PostHog/PostHog/Replay/CGSize+Util.swift deleted file mode 100644 index 2fb8380..0000000 --- a/Pods/PostHog/PostHog/Replay/CGSize+Util.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// CGSize+Util.swift -// PostHog -// -// Created by Manoel Aranda Neto on 24.07.24. -// - -#if os(iOS) - import Foundation - - extension CGSize { - func hasSize() -> Bool { - if width == 0 || height == 0 { - return false - } - return true - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Date+Util.swift b/Pods/PostHog/PostHog/Replay/Date+Util.swift deleted file mode 100644 index 4cb98d9..0000000 --- a/Pods/PostHog/PostHog/Replay/Date+Util.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Date+Util.swift -// PostHog -// -// Created by Manoel Aranda Neto on 21.03.24. -// - -import Foundation - -extension Date { - func toMillis() -> Int64 { - Int64(timeIntervalSince1970 * 1000) - } -} - -public func dateToMillis(_ date: Date) -> Int64 { - date.toMillis() -} diff --git a/Pods/PostHog/PostHog/Replay/Float+Util.swift b/Pods/PostHog/PostHog/Replay/Float+Util.swift deleted file mode 100644 index ee3eabe..0000000 --- a/Pods/PostHog/PostHog/Replay/Float+Util.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Float+Util.swift -// PostHog -// -// Created by Yiannis Josephides on 07/02/2025. -// - -import Foundation - -extension CGFloat { - func toInt() -> Int { - NSNumber(value: rounded()).intValue - } -} - -extension Double { - func toInt() -> Int { - NSNumber(value: rounded()).intValue - } -} diff --git a/Pods/PostHog/PostHog/Replay/MethodSwizzler.swift b/Pods/PostHog/PostHog/Replay/MethodSwizzler.swift deleted file mode 100644 index 1aa6c44..0000000 --- a/Pods/PostHog/PostHog/Replay/MethodSwizzler.swift +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2019-Present Datadog, Inc. - */ - -#if os(iOS) - import Foundation - - class MethodSwizzler { - struct FoundMethod: Hashable { - let method: Method - private let klass: AnyClass - - fileprivate init(method: Method, klass: AnyClass) { - self.method = method - self.klass = klass - } - - static func == (lhs: FoundMethod, rhs: FoundMethod) -> Bool { - let methodParity = (lhs.method == rhs.method) - let classParity = (NSStringFromClass(lhs.klass) == NSStringFromClass(rhs.klass)) - return methodParity && classParity - } - - func hash(into hasher: inout Hasher) { - let methodName = NSStringFromSelector(method_getName(method)) - let klassName = NSStringFromClass(klass) - let identifier = "\(methodName)|||\(klassName)" - hasher.combine(identifier) - } - } - - private var implementationCache: [FoundMethod: IMP] = [:] - var swizzledMethods: [FoundMethod] { - Array(implementationCache.keys) - } - - static func findMethod(with selector: Selector, in klass: AnyClass) throws -> FoundMethod { - /// NOTE: RUMM-452 as we never add/remove methods/classes at runtime, - /// search operation doesn't have to wrapped in sync {...} although it's visible in the interface - var headKlass: AnyClass? = klass - while let someKlass = headKlass { - if let foundMethod = findMethod(with: selector, in: someKlass) { - return FoundMethod(method: foundMethod, klass: someKlass) - } - headKlass = class_getSuperclass(headKlass) - } - throw InternalPostHogError(description: "\(NSStringFromSelector(selector)) is not found in \(NSStringFromClass(klass))") - } - - func originalImplementation(of found: FoundMethod) -> TypedIMP { - sync { - let originalImp: IMP = implementationCache[found] ?? method_getImplementation(found.method) - return unsafeBitCast(originalImp, to: TypedIMP.self) - } - } - - func swizzle( - _ foundMethod: FoundMethod, - impProvider: (TypedIMP) -> TypedBlockIMP - ) { - sync { - let currentIMP = method_getImplementation(foundMethod.method) - let currentTypedIMP = unsafeBitCast(currentIMP, to: TypedIMP.self) - let newImpBlock: TypedBlockIMP = impProvider(currentTypedIMP) - let newImp: IMP = imp_implementationWithBlock(newImpBlock) - - set(newIMP: newImp, for: foundMethod) - } - } - - /// Removes swizzling and resets the method to its original implementation. - func unswizzle() { - for foundMethod in swizzledMethods { - let originalTypedIMP = originalImplementation(of: foundMethod) - let originalIMP: IMP = unsafeBitCast(originalTypedIMP, to: IMP.self) - method_setImplementation(foundMethod.method, originalIMP) - } - } - - // MARK: - Private methods - - @discardableResult - private func sync(block: () -> T) -> T { - objc_sync_enter(self) - defer { objc_sync_exit(self) } - return block() - } - - private static func findMethod(with selector: Selector, in klass: AnyClass) -> Method? { - var methodsCount: UInt32 = 0 - let methodsCountPtr = withUnsafeMutablePointer(to: &methodsCount) { $0 } - guard let methods: UnsafeMutablePointer = class_copyMethodList(klass, methodsCountPtr) else { - return nil - } - defer { - free(methods) - } - for index in 0 ..< Int(methodsCount) { - let method = methods.advanced(by: index).pointee - if method_getName(method) == selector { - return method - } - } - return nil - } - - private func set(newIMP: IMP, for found: FoundMethod) { - if implementationCache[found] == nil { - implementationCache[found] = method_getImplementation(found.method) - } - method_setImplementation(found.method, newIMP) - } - } - - extension MethodSwizzler.FoundMethod { - var swizzlingName: String { "\(klass).\(method_getName(method))" } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/NetworkSample.swift b/Pods/PostHog/PostHog/Replay/NetworkSample.swift deleted file mode 100644 index 5c6bf4d..0000000 --- a/Pods/PostHog/PostHog/Replay/NetworkSample.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// NetworkSample.swift -// PostHog -// -// Created by Manoel Aranda Neto on 26.03.24. -// - -#if os(iOS) - import Foundation - - struct NetworkSample { - let sessionId: String - let timeOrigin: Date - let entryType = "resource" - var name: String? - var responseStatus: Int? - var initiatorType = "fetch" - var httpMethod: String? - var duration: Int64? - var decodedBodySize: Int64? - - init(sessionId: String, timeOrigin: Date, url: String? = nil) { - self.timeOrigin = timeOrigin - self.sessionId = sessionId - name = url - } - - func toDict() -> [String: Any] { - var dict: [String: Any] = [ - "timestamp": timeOrigin.toMillis(), - "entryType": entryType, - "initiatorType": initiatorType, - ] - - if let name = name { - dict["name"] = name - } - - if let responseStatus = responseStatus { - dict["responseStatus"] = responseStatus - } - - if let httpMethod = httpMethod { - dict["method"] = httpMethod - } - - if let duration = duration { - dict["duration"] = duration - } - - if let decodedBodySize = decodedBodySize { - dict["transferSize"] = decodedBodySize - } - - return dict - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Optional+Util.swift b/Pods/PostHog/PostHog/Replay/Optional+Util.swift deleted file mode 100644 index 65a6038..0000000 --- a/Pods/PostHog/PostHog/Replay/Optional+Util.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Optional+Util.swift -// PostHog -// -// Created by Yiannis Josephides on 20/01/2025. -// - -extension Optional where Wrapped: Collection { - var isNilOrEmpty: Bool { - self?.isEmpty ?? true - } -} diff --git a/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogConsoleLogInterceptor.swift b/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogConsoleLogInterceptor.swift deleted file mode 100644 index eef36ad..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogConsoleLogInterceptor.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// PostHogConsoleLogInterceptor.swift -// PostHog -// -// Created by Ioannis Josephides on 05/05/2025. -// - -#if os(iOS) - import Foundation - - final class PostHogConsoleLogInterceptor { - private let maxLogStringSize = 2000 // Maximum number of characters allowed in a string - - struct ConsoleOutput { - let timestamp: Date - let text: String - let level: PostHogLogLevel - } - - static let shared = PostHogConsoleLogInterceptor() - - // Pipe redirection properties - private var stdoutPipe: Pipe? - private var stderrPipe: Pipe? - private var originalStdout: Int32 = -1 - private var originalStderr: Int32 = -1 - - private init() { /* Singleton */ } - - func startCapturing(config: PostHogConfig, callback: @escaping (ConsoleOutput) -> Void) { - stopCapturing() // cleanup - setupPipeRedirection(config: config, callback: callback) - } - - private func setupPipeRedirection(config: PostHogConfig, callback: @escaping (ConsoleOutput) -> Void) { - // Set stdout/stderr to unbuffered mode (_IONBF) to ensure real-time output capture. - // Without this, output might be buffered and only flushed when the buffer is full or - // when explicitly flushed, which is especially problematic without an attached debugger - setvbuf(stdout, nil, _IONBF, 0) - setvbuf(stderr, nil, _IONBF, 0) - - // Save original file descriptors - originalStdout = dup(STDOUT_FILENO) - originalStderr = dup(STDERR_FILENO) - - stdoutPipe = Pipe() - stderrPipe = Pipe() - - guard let stdoutPipe = stdoutPipe, let stderrPipe = stderrPipe else { return } - - // Redirect stdout and stderr to our pipes - dup2(stdoutPipe.fileHandleForWriting.fileDescriptor, STDOUT_FILENO) - dup2(stderrPipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO) - - // Setup and handle pipe output - setupPipeSource(for: originalStdout, fileHandle: stdoutPipe.fileHandleForReading, config: config, callback: callback) - setupPipeSource(for: originalStderr, fileHandle: stderrPipe.fileHandleForReading, config: config, callback: callback) - } - - private func setupPipeSource(for originalFd: Int32, fileHandle: FileHandle, config: PostHogConfig, callback: @escaping (ConsoleOutput) -> Void) { - fileHandle.readabilityHandler = { [weak self] handle in - let data = handle.availableData - guard !data.isEmpty, - let output = String(data: data, encoding: .utf8), - let self = self else { return } - - // Write to original file descriptor, so logs appear normally - if originalFd != -1 { - if let data = output.data(using: .utf8) { - _ = data.withUnsafeBytes { ptr in - write(originalFd, ptr.baseAddress, ptr.count) - } - } - } - - self.processOutput(output, config: config, callback: callback) - } - } - - private func processOutput(_ output: String, config: PostHogConfig, callback: @escaping (ConsoleOutput) -> Void) { - // Skip internal logs and empty lines - // Note: Need to skip internal logs because `config.debug` may be enabled. If that's the case, then - // the process of capturing logs, will generate more logs, leading to an infinite loop. This relies on hedgeLog() format which should - // be okay, even not ideal - guard !output.contains("[PostHog]"), !output.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { - return - } - - // Process log entries from config - let entries = output - .components(separatedBy: CharacterSet.newlines) // split by line - .lazy - .filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } // Skip empty strings and new lines - .compactMap(config.sessionReplayConfig.captureLogsConfig.logSanitizer) - - for entry in entries where shouldCaptureLog(entry: entry, config: config) { - callback(ConsoleOutput(timestamp: Date(), text: truncatedOutput(entry.message), level: entry.level)) - } - } - - /// Determines if the log message should be captured, based on config - private func shouldCaptureLog(entry: PostHogLogEntry, config: PostHogConfig) -> Bool { - entry.level.rawValue >= config.sessionReplayConfig.captureLogsConfig.minLogLevel.rawValue - } - - /// Console logs can be really large. - /// This function returns a truncated version of the console output if it exceeds `maxLogStringSize` - private func truncatedOutput(_ output: String) -> String { - guard output.count > maxLogStringSize else { return output } - return "\(output.prefix(maxLogStringSize))...[truncated]" - } - - func stopCapturing() { - // Restore original file descriptors - if originalStdout != -1 { - dup2(originalStdout, STDOUT_FILENO) - close(originalStdout) - originalStdout = -1 - } - - if originalStderr != -1 { - dup2(originalStderr, STDERR_FILENO) - close(originalStderr) - originalStderr = -1 - } - - // remove pipes - stdoutPipe?.fileHandleForReading.readabilityHandler = nil - stderrPipe?.fileHandleForReading.readabilityHandler = nil - stdoutPipe?.fileHandleForReading.closeFile() - stderrPipe?.fileHandleForReading.closeFile() - stdoutPipe = nil - stderrPipe = nil - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogLogEntry.swift b/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogLogEntry.swift deleted file mode 100644 index 1d9ed8f..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogLogEntry.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// PostHogLogEntry.swift -// PostHog -// -// Created by Ioannis Josephides on 09/05/2025. -// - -#if os(iOS) - import Foundation - - /** - A model representing a processed console log entry for session replay. - - Describes a single console log entry after it has been processed by `PostHogSessionReplayConsoleLogConfig.logSanitizer`. - Each instance contains the log message content and its determined severity level. - */ - @objc public class PostHogLogEntry: NSObject { - /// The severity level of the log entry. - /// This determines how the log will be displayed in the session replay and - /// whether it will be captured based on `minLogLevel` setting. - @objc public let level: PostHogLogLevel - - /// The actual content of the log message. - /// This is the processed and sanitized log message - @objc public let message: String - - /// Creates a new console log result. - /// - Parameters: - /// - level: The severity level of the log entry - /// - message: The processed log message content - @objc public init(level: PostHogLogLevel, message: String) { - self.level = level - self.message = message - super.init() - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogLogLevel.swift b/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogLogLevel.swift deleted file mode 100644 index ff3f23d..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogLogLevel.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// PostHogLogLevel.swift -// PostHog -// -// Created by Ioannis Josephides on 09/05/2025. -// - -#if os(iOS) - import Foundation - - /// The severity level of a console log entry. - /// - /// Used to categorize logs by their severity in session replay. - @objc public enum PostHogLogLevel: Int { - /// Informational messages, debugging output, and general logs - case info - /// Warning messages indicating potential issues or deprecation notices - case warn - /// Error messages indicating failures or critical issues - case error - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogSessionReplayConsoleLogsPlugin.swift b/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogSessionReplayConsoleLogsPlugin.swift deleted file mode 100644 index a7f39ad..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/Console Logs/PostHogSessionReplayConsoleLogsPlugin.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// PostHogSessionReplayConsoleLogsPlugin.swift -// PostHog -// -// Created by Ioannis Josephides on 09/05/2025. -// - -#if os(iOS) - import Foundation - - final class PostHogSessionReplayConsoleLogsPlugin: PostHogSessionReplayPlugin { - private weak var postHog: PostHogSDK? - private var isActive = false - - func start(postHog: PostHogSDK) { - self.postHog = postHog - isActive = true - PostHogConsoleLogInterceptor.shared.startCapturing(config: postHog.config) { [weak self] output in - self?.handleConsoleLog(output) - } - hedgeLog("[Session Replay] Console logs plugin started") - } - - func stop() { - postHog = nil - isActive = false - PostHogConsoleLogInterceptor.shared.stopCapturing() - hedgeLog("[Session Replay] Console logs plugin stopped") - } - - func resume() { - guard !isActive, let postHog else { return } - isActive = true - PostHogConsoleLogInterceptor.shared.startCapturing(config: postHog.config) { [weak self] output in - self?.handleConsoleLog(output) - } - hedgeLog("[Session Replay] Console logs plugin resumed") - } - - func pause() { - guard isActive else { return } - isActive = false - PostHogConsoleLogInterceptor.shared.stopCapturing() - hedgeLog("[Session Replay] Console logs plugin paused") - } - - private func handleConsoleLog(_ output: PostHogConsoleLogInterceptor.ConsoleOutput) { - guard - isActive, - let postHog, - postHog.isSessionReplayActive(), - let sessionId = postHog.sessionManager.getSessionId(at: output.timestamp) - else { - return - } - - // `PostHogLogLevel`` needs to be an Int enum for objc interop - // So we need to convert this to a String before sending upstream - let level = switch output.level { - case .error: "error" - case .info: "info" - case .warn: "warn" - } - - var snapshotsData: [Any] = [] - let payloadData: [String: Any] = ["level": level, "payload": output.text] - let pluginData: [String: Any] = ["plugin": "rrweb/console@1", "payload": payloadData] - - snapshotsData.append([ - "type": 6, - "data": pluginData, - "timestamp": output.timestamp.toMillis(), - ]) - - postHog.capture( - "$snapshot", - properties: [ - "$snapshot_source": "mobile", - "$snapshot_data": snapshotsData, - "$session_id": sessionId, - ], - timestamp: output.timestamp - ) - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Plugins/Network/PostHogSessionReplayNetworkPlugin.swift b/Pods/PostHog/PostHog/Replay/Plugins/Network/PostHogSessionReplayNetworkPlugin.swift deleted file mode 100644 index af31790..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/Network/PostHogSessionReplayNetworkPlugin.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// PostHogSessionReplayNetworkPlugin.swift -// PostHog -// -// Created by Ioannis Josephides on 28/05/2025. -// - -#if os(iOS) - import Foundation - - /// Session replay plugin that captures network requests using URLSession swizzling. - class PostHogSessionReplayNetworkPlugin: PostHogSessionReplayPlugin { - private var sessionSwizzler: URLSessionSwizzler? - private var postHog: PostHogSDK? - private var isActive = false - - func start(postHog: PostHogSDK) { - self.postHog = postHog - do { - sessionSwizzler = try URLSessionSwizzler( - shouldCapture: shouldCaptureNetworkSample, - onCapture: handleNetworkSample, - getSessionId: { [weak self] date in - self?.postHog?.sessionManager.getSessionId(at: date) - } - ) - sessionSwizzler?.swizzle() - hedgeLog("[Session Replay] Network telemetry plugin started") - isActive = true - } catch { - hedgeLog("[Session Replay] Failed to initialize network telemetry: \(error)") - } - } - - func stop() { - sessionSwizzler?.unswizzle() - sessionSwizzler = nil - postHog = nil - isActive = false - hedgeLog("[Session Replay] Network telemetry plugin stopped") - } - - func resume() { - guard !isActive else { return } - isActive = true - hedgeLog("[Session Replay] Network telemetry plugin resumed") - } - - func pause() { - guard isActive else { return } - isActive = false - hedgeLog("[Session Replay] Network telemetry plugin paused") - } - - private func shouldCaptureNetworkSample() -> Bool { - guard let postHog else { return false } - return isActive && postHog.config.sessionReplayConfig.captureNetworkTelemetry && postHog.isSessionReplayActive() - } - - private func handleNetworkSample(sample: NetworkSample) { - guard let postHog else { return } - - let timestamp = sample.timeOrigin - - var snapshotsData: [Any] = [] - - let requestsData = [sample.toDict()] - let payloadData: [String: Any] = ["requests": requestsData] - let pluginData: [String: Any] = ["plugin": "rrweb/network@1", "payload": payloadData] - - let data: [String: Any] = [ - "type": 6, - "data": pluginData, - "timestamp": timestamp.toMillis(), - ] - snapshotsData.append(data) - - postHog.capture( - "$snapshot", - properties: [ - "$snapshot_source": "mobile", - "$snapshot_data": snapshotsData, - "$session_id": sample.sessionId, - ], - timestamp: sample.timeOrigin - ) - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionExtension.swift b/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionExtension.swift deleted file mode 100644 index fa43ed6..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionExtension.swift +++ /dev/null @@ -1,231 +0,0 @@ -#if os(iOS) - import Foundation - - public extension URLSession { - private func getMonotonicTimeInMilliseconds() -> UInt64 { - // Get the raw mach time - let machTime = mach_absolute_time() - - // Get timebase info to convert to nanoseconds - var timebaseInfo = mach_timebase_info_data_t() - mach_timebase_info(&timebaseInfo) - - // Convert mach time to nanoseconds - let nanoTime = machTime * UInt64(timebaseInfo.numer) / UInt64(timebaseInfo.denom) - - // Convert nanoseconds to milliseconds - let milliTime = nanoTime / 1_000_000 - - return milliTime - } - - private func executeRequest(request: URLRequest? = nil, - action: () async throws -> (Data, URLResponse), - postHog: PostHogSDK?) async throws -> (Data, URLResponse) - { - let timestamp = Date() - let startMillis = getMonotonicTimeInMilliseconds() - var endMillis: UInt64? - let sessionId = postHog?.sessionManager.getSessionId(at: timestamp) - do { - let (data, response) = try await action() - endMillis = getMonotonicTimeInMilliseconds() - captureData(request: request, - response: response, - sessionId: sessionId, - timestamp: timestamp, - start: startMillis, - end: endMillis, - postHog: postHog) - return (data, response) - } catch { - captureData(request: request, - response: nil, - sessionId: sessionId, - timestamp: timestamp, - start: startMillis, - end: endMillis, - postHog: postHog) - throw error - } - } - - private func executeRequest(request: URLRequest? = nil, - action: () async throws -> (URL, URLResponse), - postHog: PostHogSDK?) async throws -> (URL, URLResponse) - { - let timestamp = Date() - let startMillis = getMonotonicTimeInMilliseconds() - var endMillis: UInt64? - let sessionId = postHog?.sessionManager.getSessionId(at: timestamp) - do { - let (url, response) = try await action() - endMillis = getMonotonicTimeInMilliseconds() - captureData(request: request, - response: response, - sessionId: sessionId, - timestamp: timestamp, - start: startMillis, - end: endMillis, - postHog: postHog) - return (url, response) - } catch { - captureData(request: request, - response: nil, - sessionId: sessionId, - timestamp: timestamp, - start: startMillis, - end: endMillis, - postHog: postHog) - throw error - } - } - - func postHogData(for request: URLRequest, postHog: PostHogSDK? = nil) async throws -> (Data, URLResponse) { - try await executeRequest(request: request, action: { try await data(for: request) }, postHog: postHog) - } - - func postHogData(from url: URL, postHog: PostHogSDK? = nil) async throws -> (Data, URLResponse) { - try await executeRequest(action: { try await data(from: url) }, postHog: postHog) - } - - func postHogUpload( - for request: URLRequest, - fromFile fileURL: URL, - postHog: PostHogSDK? = nil - ) async throws -> (Data, URLResponse) { - try await executeRequest(request: request, action: { try await upload(for: request, fromFile: fileURL) }, postHog: postHog) - } - - func postHogUpload( - for request: URLRequest, - from bodyData: Data, - postHog: PostHogSDK? = nil - ) async throws -> (Data, URLResponse) { - try await executeRequest(request: request, action: { try await upload(for: request, from: bodyData) }, postHog: postHog) - } - - @available(iOS 15.0, *) - func postHogData( - for request: URLRequest, - delegate: (any URLSessionTaskDelegate)? = nil, - postHog: PostHogSDK? = nil - ) async throws -> (Data, URLResponse) { - try await executeRequest(request: request, action: { try await data(for: request, delegate: delegate) }, postHog: postHog) - } - - @available(iOS 15.0, *) - func postHogData( - from url: URL, - delegate: (any URLSessionTaskDelegate)? = nil, - postHog: PostHogSDK? = nil - ) async throws -> (Data, URLResponse) { - try await executeRequest(action: { try await data(from: url, delegate: delegate) }, postHog: postHog) - } - - @available(iOS 15.0, *) - func postHogUpload( - for request: URLRequest, - fromFile fileURL: URL, - delegate: (any URLSessionTaskDelegate)? = nil, - postHog: PostHogSDK? = nil - ) async throws -> (Data, URLResponse) { - try await executeRequest(request: request, action: { try await upload(for: request, fromFile: fileURL, delegate: delegate) }, postHog: postHog) - } - - @available(iOS 15.0, *) - func postHogUpload( - for request: URLRequest, - from bodyData: Data, - delegate: (any URLSessionTaskDelegate)? = nil, - postHog: PostHogSDK? = nil - ) async throws -> (Data, URLResponse) { - try await executeRequest(request: request, action: { try await upload(for: request, from: bodyData, delegate: delegate) }, postHog: postHog) - } - - @available(iOS 15.0, *) - func postHogDownload( - for request: URLRequest, - delegate: (any URLSessionTaskDelegate)? = nil, - postHog: PostHogSDK? = nil - ) async throws -> (URL, URLResponse) { - try await executeRequest(request: request, action: { try await download(for: request, delegate: delegate) }, postHog: postHog) - } - - @available(iOS 15.0, *) - func postHogDownload( - from url: URL, - delegate: (any URLSessionTaskDelegate)? = nil, - postHog: PostHogSDK? = nil - ) async throws -> (URL, URLResponse) { - try await executeRequest(action: { try await download(from: url, delegate: delegate) }, postHog: postHog) - } - - @available(iOS 15.0, *) - func postHogDownload( - resumeFrom resumeData: Data, - delegate: (any URLSessionTaskDelegate)? = nil, - postHog: PostHogSDK? = nil - ) async throws -> (URL, URLResponse) { - try await executeRequest(action: { try await download(resumeFrom: resumeData, delegate: delegate) }, postHog: postHog) - } - - // MARK: Private methods - - private func captureData( - request: URLRequest? = nil, - response: URLResponse? = nil, - sessionId: String?, - timestamp: Date, - start: UInt64, - end: UInt64? = nil, - postHog: PostHogSDK? - ) { - let instance = postHog ?? PostHogSDK.shared - - // we don't check config.sessionReplayConfig.captureNetworkTelemetry here since this extension - // has to be called manually anyway - guard let sessionId, instance.isSessionReplayActive() else { - return - } - let currentEnd = end ?? getMonotonicTimeInMilliseconds() - - PostHogReplayIntegration.dispatchQueue.async { - var snapshotsData: [Any] = [] - - var requestsData: [String: Any] = ["duration": currentEnd - start, - "method": request?.httpMethod ?? "GET", - "name": request?.url?.absoluteString ?? (response?.url?.absoluteString ?? ""), - "initiatorType": "fetch", - "entryType": "resource", - "timestamp": timestamp.toMillis()] - - // the UI special case if the transferSize is 0 as coming from cache - let transferSize = Int64(request?.httpBody?.count ?? 0) + (response?.expectedContentLength ?? 0) - if transferSize > 0 { - requestsData["transferSize"] = transferSize - } - - if let urlResponse = response as? HTTPURLResponse { - requestsData["responseStatus"] = urlResponse.statusCode - } - - let payloadData: [String: Any] = ["requests": [requestsData]] - let pluginData: [String: Any] = ["plugin": "rrweb/network@1", "payload": payloadData] - - let recordingData: [String: Any] = ["type": 6, "data": pluginData, "timestamp": timestamp.toMillis()] - snapshotsData.append(recordingData) - - instance.capture( - "$snapshot", - properties: [ - "$snapshot_source": "mobile", - "$snapshot_data": snapshotsData, - "$session_id": sessionId, - ], - timestamp: timestamp - ) - } - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionInterceptor.swift b/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionInterceptor.swift deleted file mode 100644 index b073e80..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionInterceptor.swift +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2019-Present Datadog, Inc. - */ - -#if os(iOS) - - import Foundation - - class URLSessionInterceptor { - private let tasksLock = NSLock() - private let shouldCapture: () -> Bool - private let onCapture: (NetworkSample) -> Void - private let getSessionId: (Date) -> String? - - init(shouldCapture: @escaping () -> Bool, onCapture: @escaping (NetworkSample) -> Void, getSessionId: @escaping (Date) -> String?) { - self.shouldCapture = shouldCapture - self.onCapture = onCapture - self.getSessionId = getSessionId - } - - /// An internal queue for synchronising the access to `samplesByTask`. - private let queue = DispatchQueue(label: "com.posthog.URLSessionInterceptor", target: .global(qos: .utility)) - private var samplesByTask: [URLSessionTask: NetworkSample] = [:] - - // MARK: - Interception Flow - - /// Notifies the `URLSessionTask` creation. - /// This method should be called as soon as the task was created. - /// - Parameter task: the task object obtained from `URLSession`. - func taskCreated(task: URLSessionTask, session _: URLSession? = nil) { - guard shouldCapture() else { - return - } - - guard let request = task.originalRequest else { - return - } - - guard let url = request.url else { - return - } - - let date = now() - - guard let sessionId = getSessionId(date) else { - return - } - - queue.async { - let sample = NetworkSample( - sessionId: sessionId, - timeOrigin: date, - url: url.absoluteString - ) - - self.tasksLock.withLock { - self.samplesByTask[task] = sample - } - - self.finishAll() - } - } - - /// Notifies the `URLSessionTask` completion. - /// This method should be called as soon as the task was completed. - /// - Parameter task: the task object obtained from `URLSession`. - /// - Parameter error: optional `Error` if the task completed with error. - func taskCompleted(task: URLSessionTask, error _: Error?) { - guard shouldCapture() else { - return - } - - let date = Date() - - queue.async { - var sampleTask: NetworkSample? - self.tasksLock.withLock { - sampleTask = self.samplesByTask[task] - } - - guard var sample = sampleTask else { - return - } - - self.finish(task: task, sample: &sample, date: date) - - self.finishAll() - } - } - - private func finish(task: URLSessionTask, sample: inout NetworkSample, date: Date? = nil) { - // only safe guard, should not happen - guard let request = task.originalRequest else { - tasksLock.withLock { - _ = samplesByTask.removeValue(forKey: task) - } - return - } - - let responseStatusCode = urlResponseStatusCode(response: task.response) - - if responseStatusCode != -1 { - sample.responseStatus = responseStatusCode - } - - sample.httpMethod = request.httpMethod - sample.initiatorType = "fetch" - // instrumented requests that dont use the completion handler wont have the duration set - if let date = date { - sample.duration = (date.toMillis() - sample.timeOrigin.toMillis()) - } - - // the UI special case if the transferSize is 0 as coming from cache - let transferSize = Int64(request.httpBody?.count ?? 0) + (task.response?.expectedContentLength ?? 0) - if transferSize > 0 { - sample.decodedBodySize = transferSize - } - - finish(task: task, sample: sample) - } - - // MARK: - Private - - private func urlResponseStatusCode(response: URLResponse?) -> Int { - if let urlResponse = response as? HTTPURLResponse { - return urlResponse.statusCode - } - return -1 - } - - private func finish(task: URLSessionTask, sample: NetworkSample) { - if shouldCapture() { - onCapture(sample) - } - - tasksLock.withLock { - _ = samplesByTask.removeValue(forKey: task) - } - } - - private func finishAll() { - var completedTasks: [URLSessionTask: NetworkSample] = [:] - tasksLock.withLock { - for item in samplesByTask where item.key.state == .completed { - completedTasks[item.key] = item.value - } - } - - for item in completedTasks { - var value = item.value - finish(task: item.key, sample: &value) - } - } - - func stop() { - tasksLock.withLock { - samplesByTask.removeAll() - } - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionSwizzler.swift b/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionSwizzler.swift deleted file mode 100644 index 529f8e0..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/Network/URLSessionSwizzler.swift +++ /dev/null @@ -1,251 +0,0 @@ -// swiftlint:disable nesting - -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2019-Present Datadog, Inc. - */ - -#if os(iOS) - - import Foundation - - class URLSessionSwizzler { - /// `URLSession.dataTask(with:completionHandler:)` (for `URLRequest`) swizzling. - private let dataTaskWithURLRequestAndCompletion: DataTaskWithURLRequestAndCompletion - /// `URLSession.dataTask(with:)` (for `URLRequest`) swizzling. - private let dataTaskWithURLRequest: DataTaskWithURLRequest - - /// `URLSession.dataTask(with:completionHandler:)` (for `URL`) swizzling. Only applied on iOS 13 and above. - private let dataTaskWithURLAndCompletion: DataTaskWithURLAndCompletion? - /// `URLSession.dataTask(with:)` (for `URL`) swizzling. Only applied on iOS 13 and above. - private let dataTaskWithURL: DataTaskWithURL? - - private let interceptor: URLSessionInterceptor - - private var hasSwizzled = false - - init(shouldCapture: @escaping () -> Bool, onCapture: @escaping (NetworkSample) -> Void, getSessionId: @escaping (Date) -> String?) throws { - interceptor = URLSessionInterceptor( - shouldCapture: shouldCapture, - onCapture: onCapture, - getSessionId: getSessionId - ) - - dataTaskWithURLAndCompletion = try DataTaskWithURLAndCompletion.build(interceptor: interceptor) - dataTaskWithURL = try DataTaskWithURL.build(interceptor: interceptor) - - dataTaskWithURLRequestAndCompletion = try DataTaskWithURLRequestAndCompletion.build(interceptor: interceptor) - dataTaskWithURLRequest = try DataTaskWithURLRequest.build(interceptor: interceptor) - } - - func swizzle() { - dataTaskWithURLRequestAndCompletion.swizzle() - dataTaskWithURLAndCompletion?.swizzle() - dataTaskWithURLRequest.swizzle() - dataTaskWithURL?.swizzle() - hasSwizzled = true - } - - func unswizzle() { - if !hasSwizzled { - return - } - dataTaskWithURLRequestAndCompletion.unswizzle() - dataTaskWithURLRequest.unswizzle() - dataTaskWithURLAndCompletion?.unswizzle() - dataTaskWithURL?.unswizzle() - hasSwizzled = false - } - - // MARK: - Swizzlings - - typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void - - /// Swizzles the `URLSession.dataTask(with:completionHandler:)` for `URLRequest`. - class DataTaskWithURLRequestAndCompletion: MethodSwizzler< - @convention(c) (URLSession, Selector, URLRequest, CompletionHandler?) -> URLSessionDataTask, - @convention(block) (URLSession, URLRequest, CompletionHandler?) -> URLSessionDataTask - > { - private static let selector = #selector( - URLSession.dataTask(with:completionHandler:) as (URLSession) -> (URLRequest, @escaping CompletionHandler) -> URLSessionDataTask - ) - - private let method: FoundMethod - private let interceptor: URLSessionInterceptor - - static func build(interceptor: URLSessionInterceptor) throws -> DataTaskWithURLRequestAndCompletion { - try DataTaskWithURLRequestAndCompletion( - selector: selector, - klass: URLSession.self, - interceptor: interceptor - ) - } - - private init(selector: Selector, klass: AnyClass, interceptor: URLSessionInterceptor) throws { - method = try Self.findMethod(with: selector, in: klass) - self.interceptor = interceptor - super.init() - } - - func swizzle() { - typealias Signature = @convention(block) (URLSession, URLRequest, CompletionHandler?) -> URLSessionDataTask - swizzle(method) { previousImplementation -> Signature in { session, urlRequest, completionHandler -> URLSessionDataTask in - let task: URLSessionDataTask - if completionHandler != nil { - var taskReference: URLSessionDataTask? - let newCompletionHandler: CompletionHandler = { data, response, error in - if let task = taskReference { // sanity check, should always succeed - self.interceptor.taskCompleted(task: task, error: error) - } - completionHandler?(data, response, error) - } - - task = previousImplementation(session, Self.selector, urlRequest, newCompletionHandler) - taskReference = task - } else { - // The `completionHandler` can be `nil` in two cases: - // - on iOS 11 or 12, where `dataTask(with:)` (for `URL` and `URLRequest`) calls - // the `dataTask(with:completionHandler:)` (for `URLRequest`) internally by nullifying the completion block. - // - when `[session dataTaskWithURL:completionHandler:]` is called in Objective-C with explicitly passing - // `nil` as the `completionHandler` (it produces a warning, but compiles). - task = previousImplementation(session, Self.selector, urlRequest, completionHandler) - } - self.interceptor.taskCreated(task: task, session: session) - return task - } - } - } - } - - /// Swizzles the `URLSession.dataTask(with:completionHandler:)` for `URL`. - class DataTaskWithURLAndCompletion: MethodSwizzler< - @convention(c) (URLSession, Selector, URL, CompletionHandler?) -> URLSessionDataTask, - @convention(block) (URLSession, URL, CompletionHandler?) -> URLSessionDataTask - > { - private static let selector = #selector( - URLSession.dataTask(with:completionHandler:) as (URLSession) -> (URL, @escaping CompletionHandler) -> URLSessionDataTask - ) - - private let method: FoundMethod - private let interceptor: URLSessionInterceptor - - static func build(interceptor: URLSessionInterceptor) throws -> DataTaskWithURLAndCompletion { - try DataTaskWithURLAndCompletion( - selector: selector, - klass: URLSession.self, - interceptor: interceptor - ) - } - - private init(selector: Selector, klass: AnyClass, interceptor: URLSessionInterceptor) throws { - method = try Self.findMethod(with: selector, in: klass) - self.interceptor = interceptor - super.init() - } - - func swizzle() { - typealias Signature = @convention(block) (URLSession, URL, CompletionHandler?) -> URLSessionDataTask - swizzle(method) { previousImplementation -> Signature in { session, url, completionHandler -> URLSessionDataTask in - let task: URLSessionDataTask - if completionHandler != nil { - var taskReference: URLSessionDataTask? - let newCompletionHandler: CompletionHandler = { data, response, error in - if let task = taskReference { // sanity check, should always succeed - self.interceptor.taskCompleted(task: task, error: error) - } - completionHandler?(data, response, error) - } - task = previousImplementation(session, Self.selector, url, newCompletionHandler) - taskReference = task - } else { - // The `completionHandler` can be `nil` in one case: - // - when `[session dataTaskWithURL:completionHandler:]` is called in Objective-C with explicitly passing - // `nil` as the `completionHandler` (it produces a warning, but compiles). - task = previousImplementation(session, Self.selector, url, completionHandler) - } - self.interceptor.taskCreated(task: task, session: session) - return task - } - } - } - } - - /// Swizzles the `URLSession.dataTask(with:)` for `URLRequest`. - class DataTaskWithURLRequest: MethodSwizzler< - @convention(c) (URLSession, Selector, URLRequest) -> URLSessionDataTask, - @convention(block) (URLSession, URLRequest) -> URLSessionDataTask - > { - private static let selector = #selector( - URLSession.dataTask(with:) as (URLSession) -> (URLRequest) -> URLSessionDataTask - ) - - private let method: FoundMethod - private let interceptor: URLSessionInterceptor - - static func build(interceptor: URLSessionInterceptor) throws -> DataTaskWithURLRequest { - try DataTaskWithURLRequest( - selector: selector, - klass: URLSession.self, - interceptor: interceptor - ) - } - - private init(selector: Selector, klass: AnyClass, interceptor: URLSessionInterceptor) throws { - method = try Self.findMethod(with: selector, in: klass) - self.interceptor = interceptor - super.init() - } - - func swizzle() { - typealias Signature = @convention(block) (URLSession, URLRequest) -> URLSessionDataTask - swizzle(method) { previousImplementation -> Signature in { session, urlRequest -> URLSessionDataTask in - let task = previousImplementation(session, Self.selector, urlRequest) - self.interceptor.taskCreated(task: task, session: session) - return task - } - } - } - } - - /// Swizzles the `URLSession.dataTask(with:)` for `URL`. - class DataTaskWithURL: MethodSwizzler< - @convention(c) (URLSession, Selector, URL) -> URLSessionDataTask, - @convention(block) (URLSession, URL) -> URLSessionDataTask - > { - private static let selector = #selector( - URLSession.dataTask(with:) as (URLSession) -> (URL) -> URLSessionDataTask - ) - - private let method: FoundMethod - private let interceptor: URLSessionInterceptor - - static func build(interceptor: URLSessionInterceptor) throws -> DataTaskWithURL { - try DataTaskWithURL( - selector: selector, - klass: URLSession.self, - interceptor: interceptor - ) - } - - private init(selector: Selector, klass: AnyClass, interceptor: URLSessionInterceptor) throws { - method = try Self.findMethod(with: selector, in: klass) - self.interceptor = interceptor - super.init() - } - - func swizzle() { - typealias Signature = @convention(block) (URLSession, URL) -> URLSessionDataTask - swizzle(method) { previousImplementation -> Signature in { session, url -> URLSessionDataTask in - let task = previousImplementation(session, Self.selector, url) - self.interceptor.taskCreated(task: task, session: session) - return task - } - } - } - } - } - -#endif - -// swiftlint:enable nesting diff --git a/Pods/PostHog/PostHog/Replay/Plugins/PostHogSessionReplayPlugin.swift b/Pods/PostHog/PostHog/Replay/Plugins/PostHogSessionReplayPlugin.swift deleted file mode 100644 index 49cbde8..0000000 --- a/Pods/PostHog/PostHog/Replay/Plugins/PostHogSessionReplayPlugin.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// PostHogSessionReplayPlugin.swift -// PostHog -// -// Created by Ioannis Josephides on 12/05/2025. -// - -#if os(iOS) - import Foundation - - /// Session replay plugins are used to capture specific types of meta data during a session, - /// such as console logs, network requests and user interactions. Each plugin is responsible - /// for managing its own capture lifecycle and sending data to PostHog. - /// - /// Plugins are installed automatically based on the session replay configuration. - protocol PostHogSessionReplayPlugin { - /// Starts the plugin and begins data capture. - /// - /// Called when session replay is started. The plugin should set up any required - /// resources and begin capturing data. - /// - /// - Parameter postHog: The PostHog SDK instance to use for sending data - func start(postHog: PostHogSDK) - - /// Stops the plugin and cleans up resources. - /// - /// Called when session replay is stopped. The plugin should clean up any resources - /// and stop capturing data. - func stop() - - /// Temporarily pauses data capture. - /// - /// Called by session replay integration when plugin is requested to temporarily pause capturing data - /// The plugin should pause data capture but maintain its state. - func pause() - - /// Resumes data capture after being paused. - /// - /// Called by session replay integration when plugin is requested to resume normal capturing data - /// The plugin should resume data capture from its previous state. - func resume() - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/PostHogReplayIntegration.swift b/Pods/PostHog/PostHog/Replay/PostHogReplayIntegration.swift deleted file mode 100644 index d3511fc..0000000 --- a/Pods/PostHog/PostHog/Replay/PostHogReplayIntegration.swift +++ /dev/null @@ -1,865 +0,0 @@ -// swiftlint:disable cyclomatic_complexity - -// -// PostHogReplayIntegration.swift -// PostHog -// -// Created by Manoel Aranda Neto on 19.03.24. -// -#if os(iOS) - import Foundation - import PhotosUI - import SwiftUI - import UIKit - import WebKit - - class PostHogReplayIntegration: PostHogIntegration { - var requiresSwizzling: Bool { true } - - private static var integrationInstalledLock = NSLock() - private static var integrationInstalled = false - - private var config: PostHogConfig? { - postHog?.config - } - - private weak var postHog: PostHogSDK? - - private var isEnabled: Bool = false - - private let windowViewsLock = NSLock() - private let windowViews = NSMapTable.weakToStrongObjects() - private var applicationEventToken: RegistrationToken? - private var applicationBackgroundedToken: RegistrationToken? - private var applicationForegroundedToken: RegistrationToken? - private var viewLayoutToken: RegistrationToken? - private var installedPlugins: [PostHogSessionReplayPlugin] = [] - - /** - ### Mapping of SwiftUI Views to UIKit - - This section summarizes findings on how SwiftUI views map to UIKit components - - #### Image-Based Views - - **`AsyncImage` and `Image`** - - Both views have a `CALayer` of type `SwiftUI.ImageLayer`. - - The associated `UIView` is of type `SwiftUI._UIGraphicsView`. - - #### Graphic-based Views - - **`Color`, `Divider`, `Gradient` etc - - These are backed by `SwiftUI._UIGraphicsView` but have a different layer type than images - - #### Text-Based Views - - **`Text`, `Button`, and `TextEditor`** - - These views are backed by a `UIView` of type `SwiftUI.CGDrawingView`, which is a subclass of `SwiftUI._UIGraphicsView`. - - CoreGraphics (`CG`) is used for rendering text content directly, making it challenging to access the value programmatically. - - #### UIKit-Mapped Views - - **Views Hosted by `UIViewRepresentable`** - - Some SwiftUI views map directly to UIKit classes or to a subclass: - - **Control Images** (e.g., in `Picker` drop-downs) may map to `UIImageView`. - - **Buttons** map to `SwiftUI.UIKitIconPreferringButton` (a subclass of `UIButton`). - - **Toggle** maps to `UISwitch` (the toggle itself, excluding its label). - - **Picker** with wheel style maps to `UIPickerView`. Other styles use combinations of image-based and text-based views. - - #### Layout and Structure Views - - **`Spacer`, `VStack`, `HStack`, `ZStack`, and Lazy Stacks** - - These views do not correspond to specific a `UIView`. Instead, they translate directly into layout constraints. - - #### List-Based Views - - **`List` and Scrollable Container Views** - - Backed by a subclass of `UICollectionView` - - #### Other SwiftUI Views - - Most other SwiftUI views are *compositions* of the views described above - - SwiftUI Image Types: - - [StackOverflow: Subviews of a Window or View in SwiftUI](https://stackoverflow.com/questions/57554590/how-to-get-all-the-subviews-of-a-window-or-view-in-latest-swiftui-app) - - [StackOverflow: Detect SwiftUI Usage Programmatically](https://stackoverflow.com/questions/58336045/how-to-detect-swiftui-usage-programmatically-in-an-ios-application) - */ - - /// `AsyncImage` and `Image` - private let swiftUIImageLayerTypes = [ - "SwiftUI.ImageLayer", - ].compactMap(NSClassFromString) - - /// `Text`, `Button`, `TextEditor` views - private let swiftUITextBasedViewTypes = [ - "SwiftUI.CGDrawingView", // Text, Button - "SwiftUI.TextEditorTextView", // TextEditor - "SwiftUI.VerticalTextView", // TextField, vertical axis - ].compactMap(NSClassFromString) - - private let swiftUIGenericTypes = [ - "_TtC7SwiftUIP33_A34643117F00277B93DEBAB70EC0697122_UIShapeHitTestingView", - ].compactMap(NSClassFromString) - - private let reactNativeTextView: AnyClass? = NSClassFromString("RCTTextView") - private let reactNativeImageView: AnyClass? = NSClassFromString("RCTImageView") - // These are usually views that don't belong to the current process and are most likely sensitive - private let systemSandboxedView: AnyClass? = NSClassFromString("_UIRemoteView") - - // These layer types should be safe to ignore while masking - private let swiftUISafeLayerTypes: [AnyClass] = [ - "SwiftUI.GradientLayer", // Views like LinearGradient, RadialGradient, or AngularGradient - ].compactMap(NSClassFromString) - - static let dispatchQueue = DispatchQueue(label: "com.posthog.PostHogReplayIntegration", - target: .global(qos: .utility)) - - private func isNotFlutter() -> Bool { - // for the Flutter SDK, screen recordings are managed by Flutter SDK itself - postHogSdkName != "posthog-flutter" - } - - func install(_ postHog: PostHogSDK) throws { - try PostHogReplayIntegration.integrationInstalledLock.withLock { - if PostHogReplayIntegration.integrationInstalled { - throw InternalPostHogError(description: "Replay integration already installed to another PostHogSDK instance.") - } - PostHogReplayIntegration.integrationInstalled = true - } - - self.postHog = postHog - - start() - } - - func uninstall(_ postHog: PostHogSDK) { - if self.postHog === postHog || self.postHog == nil { - stop() - self.postHog = nil - PostHogReplayIntegration.integrationInstalledLock.withLock { - PostHogReplayIntegration.integrationInstalled = false - } - } - } - - func start() { - guard let postHog, !isEnabled else { - return - } - - isEnabled = true - // reset views when session id changes (or is cleared) so we can re-send new metadata (or full snapshot in the future) - postHog.sessionManager.onSessionIdChanged = { [weak self] in - self?.resetViews() - } - - // flutter captures snapshots, so we don't need to capture them here - if isNotFlutter() { - let interval = postHog.config.sessionReplayConfig.throttleDelay - viewLayoutToken = DI.main.viewLayoutPublisher.onViewLayout(throttle: interval) { [weak self] in - // called on main thread - self?.snapshot() - } - } - - // start listening to `UIApplication.sendEvent` - let applicationEventPublisher = DI.main.applicationEventPublisher - applicationEventToken = applicationEventPublisher.onApplicationEvent { [weak self] event, date in - self?.handleApplicationEvent(event: event, date: date) - } - - // Install plugins - let plugins = postHog.config.sessionReplayConfig.getPlugins() - installedPlugins = [] - for plugin in plugins { - plugin.start(postHog: postHog) - installedPlugins.append(plugin) - } - - // Start listening to application background events and pause all plugins - let applicationLifecyclePublisher = DI.main.appLifecyclePublisher - applicationBackgroundedToken = applicationLifecyclePublisher.onDidEnterBackground { [weak self] in - self?.pauseAllPlugins() - } - - // Start listening to application foreground events and resume all plugins - applicationForegroundedToken = applicationLifecyclePublisher.onDidBecomeActive { [weak self] in - self?.resumeAllPlugins() - } - } - - func stop() { - guard isEnabled else { return } - isEnabled = false - resetViews() - postHog?.sessionManager.onSessionIdChanged = {} - - // stop listening to `UIApplication.sendEvent` - applicationEventToken = nil - // stop listening to Application lifecycle events - applicationBackgroundedToken = nil - applicationForegroundedToken = nil - // stop listening to `UIView.layoutSubviews` events - viewLayoutToken = nil - - // stop plugins - for plugin in installedPlugins { - plugin.stop() - } - installedPlugins = [] - } - - func isActive() -> Bool { - isEnabled - } - - private func resetViews() { - // Ensure thread-safe access to windowViews - windowViewsLock.withLock { - windowViews.removeAllObjects() - } - } - - private func pauseAllPlugins() { - for plugin in installedPlugins { - plugin.pause() - } - } - - private func resumeAllPlugins() { - for plugin in installedPlugins { - plugin.resume() - } - } - - private func handleApplicationEvent(event: UIEvent, date: Date) { - guard let postHog, postHog.isSessionReplayActive() else { - return - } - - guard event.type == .touches else { - return - } - - guard let window = UIApplication.getCurrentWindow() else { - return - } - - guard let touches = event.touches(for: window) else { - return - } - - // capture necessary touch information on the main thread before performing any asynchronous operations - // - this ensures that UITouch associated objects like UIView, UIWindow, or [UIGestureRecognizer] are still valid. - // - these objects may be released or erased by the system if accessed asynchronously, resulting in invalid/zeroed-out touch coordinates - let touchInfo = touches.map { - (phase: $0.phase, location: $0.location(in: window)) - } - - PostHogReplayIntegration.dispatchQueue.async { [touchInfo, weak postHog = postHog] in - // always make sure we have a fresh session id as early as possible - guard let sessionId = postHog?.sessionManager.getSessionId(at: date) else { - return - } - - // captured weakly since integration may have uninstalled by now - guard let postHog else { return } - - var snapshotsData: [Any] = [] - for touch in touchInfo { - let phase = touch.phase - - let type: Int - if phase == .began { - type = 7 - } else if phase == .ended { - type = 9 - } else { - continue - } - - // we keep a failsafe here just in case, but this will likely never be triggered - guard touch.location != .zero else { - continue - } - - let posX = touch.location.x.toInt() - let posY = touch.location.y.toInt() - - // if the id is 0, BE transformer will set it to the virtual bodyId - let touchData: [String: Any] = ["id": 0, "pointerType": 2, "source": 2, "type": type, "x": posX, "y": posY] - - let data: [String: Any] = ["type": 3, "data": touchData, "timestamp": date.toMillis()] - snapshotsData.append(data) - } - if !snapshotsData.isEmpty { - postHog.capture( - "$snapshot", - properties: [ - "$snapshot_source": "mobile", - "$snapshot_data": snapshotsData, - "$session_id": sessionId, - ], - timestamp: date - ) - } - } - } - - private func generateSnapshot(_ window: UIWindow, _ screenName: String? = nil, postHog: PostHogSDK) { - var hasChanges = false - - guard let wireframe = postHog.config.sessionReplayConfig.screenshotMode ? toScreenshotWireframe(window) : toWireframe(window) else { - return - } - - // capture timestamp after snapshot was taken - let timestampDate = Date() - let timestamp = timestampDate.toMillis() - - let snapshotStatus = windowViewsLock.withLock { - windowViews.object(forKey: window) ?? ViewTreeSnapshotStatus() - } - - var snapshotsData: [Any] = [] - - if !snapshotStatus.sentMetaEvent { - let size = window.bounds.size - let width = size.width.toInt() - let height = size.height.toInt() - - var data: [String: Any] = ["width": width, "height": height] - - if let screenName = screenName { - data["href"] = screenName - } - - let snapshotData: [String: Any] = ["type": 4, "data": data, "timestamp": timestamp] - snapshotsData.append(snapshotData) - snapshotStatus.sentMetaEvent = true - hasChanges = true - } - - if hasChanges { - windowViewsLock.withLock { - windowViews.setObject(snapshotStatus, forKey: window) - } - } - - // TODO: IncrementalSnapshot, type=2 - - PostHogReplayIntegration.dispatchQueue.async { - // always make sure we have a fresh session id at correct timestamp - guard let sessionId = postHog.sessionManager.getSessionId(at: timestampDate) else { - return - } - - var wireframes: [Any] = [] - wireframes.append(wireframe.toDict()) - let initialOffset = ["top": 0, "left": 0] - let data: [String: Any] = ["initialOffset": initialOffset, "wireframes": wireframes] - let snapshotData: [String: Any] = ["type": 2, "data": data, "timestamp": timestamp] - snapshotsData.append(snapshotData) - - postHog.capture( - "$snapshot", - properties: [ - "$snapshot_source": "mobile", - "$snapshot_data": snapshotsData, - "$session_id": sessionId, - ], - timestamp: timestampDate - ) - } - } - - private func setAlignment(_ alignment: NSTextAlignment, _ style: RRStyle) { - if alignment == .center { - style.verticalAlign = "center" - style.horizontalAlign = "center" - } else if alignment == .right { - style.horizontalAlign = "right" - } else if alignment == .left { - style.horizontalAlign = "left" - } - } - - private func setPadding(_ insets: UIEdgeInsets, _ style: RRStyle) { - style.paddingTop = insets.top.toInt() - style.paddingRight = insets.right.toInt() - style.paddingBottom = insets.bottom.toInt() - style.paddingLeft = insets.left.toInt() - } - - private func createBasicWireframe(_ view: UIView) -> RRWireframe { - let wireframe = RRWireframe() - - // since FE will render each node of the wireframe with position: fixed - // we need to convert bounds to global screen coordinates - // otherwise each view of depth > 1 will likely have an origin of 0,0 (which is the local origin) - let frame = view.toAbsoluteRect(view.window) - - wireframe.id = view.hash - wireframe.posX = frame.origin.x.toInt() - wireframe.posY = frame.origin.y.toInt() - wireframe.width = frame.size.width.toInt() - wireframe.height = frame.size.height.toInt() - - return wireframe - } - - private func findMaskableWidgets(_ view: UIView, _ window: UIWindow, _ maskableWidgets: inout [CGRect], _ maskChildren: inout Bool) { - // User explicitly marked this view (and its subviews) as non-maskable through `.postHogNoMask()` view modifier - if view.postHogNoMask { - return - } - - if let textView = view as? UITextView { // TextEditor, SwiftUI.TextEditorTextView, SwiftUI.UIKitTextView - if isTextViewSensitive(textView) { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - /// SwiftUI: `TextField`, `SecureField` will land here - if let textField = view as? UITextField { - if isTextFieldSensitive(textField) { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - if let reactNativeTextView = reactNativeTextView { - if view.isKind(of: reactNativeTextView), config?.sessionReplayConfig.maskAllTextInputs == true { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - /// SwiftUI: Some control images like the ones in `Picker` view may land here - if let image = view as? UIImageView { - if isImageViewSensitive(image) { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - if let reactNativeImageView = reactNativeImageView { - if view.isKind(of: reactNativeImageView), config?.sessionReplayConfig.maskAllImages == true { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - if let label = view as? UILabel { // Text, this code might never be reachable in SwiftUI, see swiftUIImageTypes instead - if isLabelSensitive(label) { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - if let webView = view as? WKWebView { // Link, this code might never be reachable in SwiftUI, see swiftUIImageTypes instead - // since we cannot mask the webview content, if masking texts or images are enabled - // we mask the whole webview as well - if isAnyInputSensitive(webView) { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - /// SwiftUI: `SwiftUI.UIKitIconPreferringButton` and other subclasses will land here - if let button = view as? UIButton { - if isButtonSensitive(button) { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - /// SwiftUI: `Toggle` (no text, labels are just rendered to Text (swiftUIImageTypes)) - if let theSwitch = view as? UISwitch { - if isSwitchSensitive(theSwitch) { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - // detect any views that don't belong to the current process (likely system views) - if config?.sessionReplayConfig.maskAllSandboxedViews == true, - let systemSandboxedView, - view.isKind(of: systemSandboxedView) - { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - - // if its a generic type and has subviews, subviews have to be checked first - let hasSubViews = !view.subviews.isEmpty - - /// SwiftUI: `Picker` with .pickerStyle(.wheel) will land here - if let picker = view as? UIPickerView { - if isTextInputSensitive(picker), !hasSubViews { - maskableWidgets.append(picker.toAbsoluteRect(window)) - return - } - } - - /// SwiftUI: Text based views like `Text`, `Button`, `TextEditor` - if swiftUITextBasedViewTypes.contains(where: view.isKind(of:)) { - if isTextInputSensitive(view), !hasSubViews { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - /// SwiftUI: Image based views like `Image`, `AsyncImage`. (Note: We check the layer type here) - if swiftUIImageLayerTypes.contains(where: view.layer.isKind(of:)) { - if isSwiftUIImageSensitive(view), !hasSubViews { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - // this can be anything, so better to be conservative - if swiftUIGenericTypes.contains(where: { view.isKind(of: $0) }), !isSwiftUILayerSafe(view.layer) { - if isTextInputSensitive(view), !hasSubViews { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - } - - // manually masked views through `.postHogMask()` view modifier - if view.postHogNoCapture { - maskableWidgets.append(view.toAbsoluteRect(window)) - return - } - - // on RN, lots get converted to RCTRootContentView, RCTRootView, RCTView and sometimes its just the whole screen, we dont want to mask - // in such cases - if view.isNoCapture() || maskChildren { - let viewRect = view.toAbsoluteRect(window) - let windowRect = window.frame - - // Check if the rectangles do not match - if !viewRect.equalTo(windowRect) { - maskableWidgets.append(view.toAbsoluteRect(window)) - } else { - maskChildren = true - } - } - - if !view.subviews.isEmpty { - for child in view.subviews { - if !child.isVisible() { - continue - } - - findMaskableWidgets(child, window, &maskableWidgets, &maskChildren) - } - } - maskChildren = false - } - - private func toScreenshotWireframe(_ window: UIWindow) -> RRWireframe? { - // this will bail on view controller animations (interactive or not) - if !window.isVisible() || isAnimatingTransition(window) { - return nil - } - - var maskableWidgets: [CGRect] = [] - var maskChildren = false - findMaskableWidgets(window, window, &maskableWidgets, &maskChildren) - - let wireframe = createBasicWireframe(window) - - if let image = window.toImage() { - if !image.size.hasSize() { - return nil - } - - wireframe.maskableWidgets = maskableWidgets - - wireframe.image = image - } - wireframe.type = "screenshot" - return wireframe - } - - /// Check if any view controller in the hierarchy is animating a transition - private func isAnimatingTransition(_ window: UIWindow) -> Bool { - guard let rootViewController = window.rootViewController else { return false } - return isAnimatingTransition(rootViewController) - } - - private func isAnimatingTransition(_ viewController: UIViewController) -> Bool { - // Check if this view controller is animating - if viewController.transitionCoordinator?.isAnimated ?? false { - return true - } - - // Check if presented view controller is animating - if let presented = viewController.presentedViewController, isAnimatingTransition(presented) { - return true - } - - // Check if any of the child view controllers is animating - if viewController.children.first(where: isAnimatingTransition) != nil { - return true - } - - return false - } - - private func isAssetsImage(_ image: UIImage) -> Bool { - // https://github.com/daydreamboy/lldb_scripts#9-pimage - // do not mask if its an asset image, likely not PII anyway - image.imageAsset?.value(forKey: "_containingBundle") != nil - } - - private func isAnyInputSensitive(_ view: UIView) -> Bool { - isTextInputSensitive(view) || config?.sessionReplayConfig.maskAllImages == true - } - - private func isTextInputSensitive(_ view: UIView) -> Bool { - config?.sessionReplayConfig.maskAllTextInputs == true || view.isNoCapture() - } - - private func isLabelSensitive(_ view: UILabel) -> Bool { - isTextInputSensitive(view) && hasText(view.text) - } - - private func isButtonSensitive(_ view: UIButton) -> Bool { - isTextInputSensitive(view) && hasText(view.titleLabel?.text) - } - - private func isTextViewSensitive(_ view: UITextView) -> Bool { - (isTextInputSensitive(view) || view.isSensitiveText()) && hasText(view.text) - } - - private func isSwitchSensitive(_ view: UISwitch) -> Bool { - var containsText = true - if #available(iOS 14.0, *) { - containsText = hasText(view.title) - } - - return isTextInputSensitive(view) && containsText - } - - private func isTextFieldSensitive(_ view: UITextField) -> Bool { - (isTextInputSensitive(view) || view.isSensitiveText()) && (hasText(view.text) || hasText(view.placeholder)) - } - - private func isSwiftUILayerSafe(_ layer: CALayer) -> Bool { - swiftUISafeLayerTypes.contains(where: { layer.isKind(of: $0) }) - } - - private func hasText(_ text: String?) -> Bool { - if let text = text, !text.isEmpty { - return true - } else { - // if there's no text, there's nothing to mask - return false - } - } - - private func isSwiftUIImageSensitive(_ view: UIView) -> Bool { - // No way of checking if this is an asset image or not - // No way of checking if there's actual content in the image or not - config?.sessionReplayConfig.maskAllImages == true || view.isNoCapture() - } - - private func isImageViewSensitive(_ view: UIImageView) -> Bool { - // if there's no image, there's nothing to mask - guard let image = view.image else { return false } - - // sensitive, regardless - if view.isNoCapture() { - return true - } - - // asset images are probably not sensitive - if isAssetsImage(image) { - return false - } - - // symbols are probably not sensitive - if image.isSymbolImage { - return false - } - - return config?.sessionReplayConfig.maskAllImages == true - } - - private func toWireframe(_ view: UIView) -> RRWireframe? { - if !view.isVisible() { - return nil - } - - let wireframe = createBasicWireframe(view) - - let style = RRStyle() - - if let textView = view as? UITextView { - wireframe.type = "text" - wireframe.text = isTextViewSensitive(textView) ? textView.text.mask() : textView.text - wireframe.disabled = !textView.isEditable - style.color = textView.textColor?.toRGBString() - style.fontFamily = textView.font?.familyName - if let fontSize = textView.font?.pointSize.toInt() { - style.fontSize = fontSize - } - setAlignment(textView.textAlignment, style) - setPadding(textView.textContainerInset, style) - } - - if let textField = view as? UITextField { - wireframe.type = "input" - wireframe.inputType = "text_area" - let isSensitive = isTextFieldSensitive(textField) - if let text = textField.text { - wireframe.value = isSensitive ? text.mask() : text - } else { - if let text = textField.placeholder { - wireframe.value = isSensitive ? text.mask() : text - } - } - wireframe.disabled = !textField.isEnabled - style.color = textField.textColor?.toRGBString() - style.fontFamily = textField.font?.familyName - if let fontSize = textField.font?.pointSize.toInt() { - style.fontSize = fontSize - } - setAlignment(textField.textAlignment, style) - } - - if view is UIPickerView { - wireframe.type = "input" - wireframe.inputType = "select" - // set wireframe.value from selected row - } - - if let theSwitch = view as? UISwitch { - wireframe.type = "input" - wireframe.inputType = "toggle" - wireframe.checked = theSwitch.isOn - if #available(iOS 14.0, *) { - if let text = theSwitch.title { - wireframe.label = isSwitchSensitive(theSwitch) ? text.mask() : text - } - } - } - - if let imageView = view as? UIImageView { - wireframe.type = "image" - if let image = imageView.image { - if !isImageViewSensitive(imageView) { - wireframe.image = image - } - } - } - - if let button = view as? UIButton { - wireframe.type = "input" - wireframe.inputType = "button" - wireframe.disabled = !button.isEnabled - - if let text = button.titleLabel?.text { - // NOTE: this will create a ghosting effect since text will also be captured in child UILabel - // We also may be masking this UIButton but child UILabel may remain unmasked - wireframe.value = isButtonSensitive(button) ? text.mask() : text - } - } - - if let label = view as? UILabel { - wireframe.type = "text" - if let text = label.text { - wireframe.text = isLabelSensitive(label) ? text.mask() : text - } - wireframe.disabled = !label.isEnabled - style.color = label.textColor?.toRGBString() - style.fontFamily = label.font?.familyName - if let fontSize = label.font?.pointSize.toInt() { - style.fontSize = fontSize - } - setAlignment(label.textAlignment, style) - } - - if view is WKWebView { - wireframe.type = "web_view" - } - - if let progressView = view as? UIProgressView { - wireframe.type = "input" - wireframe.inputType = "progress" - wireframe.value = progressView.progress - wireframe.max = 1 - // UIProgressView theres not circular format, only custom view or swiftui - style.bar = "horizontal" - } - - if view is UIActivityIndicatorView { - wireframe.type = "input" - wireframe.inputType = "progress" - style.bar = "circular" - } - - // TODO: props: backgroundImage (probably not needed) - // TODO: componenets: UITabBar, UINavigationBar, UISlider, UIStepper, UIDatePicker - - style.backgroundColor = view.backgroundColor?.toRGBString() - let layer = view.layer - style.borderWidth = layer.borderWidth.toInt() - style.borderRadius = layer.cornerRadius.toInt() - style.borderColor = layer.borderColor?.toRGBString() - - wireframe.style = style - - if !view.subviews.isEmpty { - var childWireframes: [RRWireframe] = [] - for subview in view.subviews { - if let child = toWireframe(subview) { - childWireframes.append(child) - } - } - wireframe.childWireframes = childWireframes - } - - return wireframe - } - - @objc private func snapshot() { - guard let postHog, postHog.isSessionReplayActive() else { - return - } - - guard let window = UIApplication.getCurrentWindow() else { - return - } - - var screenName: String? - if let controller = window.rootViewController { - // SwiftUI only supported with screenshotMode - if controller is AnyObjectUIHostingViewController, !postHog.config.sessionReplayConfig.screenshotMode { - hedgeLog("SwiftUI snapshot not supported, enable screenshotMode.") - return - // screen name only makes sense if we are not using SwiftUI - } else if !postHog.config.sessionReplayConfig.screenshotMode { - screenName = UIViewController.getViewControllerName(controller) - } - } - - // this cannot run off of the main thread because most properties require to be called within the main thread - // this method has to be fast and do as little as possible - generateSnapshot(window, screenName, postHog: postHog) - } - } - - private protocol AnyObjectUIHostingViewController: AnyObject {} - - extension UIHostingController: AnyObjectUIHostingViewController {} - - #if TESTING - extension PostHogReplayIntegration { - static func clearInstalls() { - integrationInstalledLock.withLock { - integrationInstalled = false - } - } - } - #endif - -#endif - -// swiftlint:enable cyclomatic_complexity diff --git a/Pods/PostHog/PostHog/Replay/PostHogSessionReplayConfig.swift b/Pods/PostHog/PostHog/Replay/PostHogSessionReplayConfig.swift deleted file mode 100644 index 9f27abf..0000000 --- a/Pods/PostHog/PostHog/Replay/PostHogSessionReplayConfig.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// PostHogSessionReplayConfig.swift -// PostHog -// -// Created by Manoel Aranda Neto on 19.03.24. -// -#if os(iOS) - import Foundation - - @objc(PostHogSessionReplayConfig) public class PostHogSessionReplayConfig: NSObject { - /// Enable masking of all text and text input fields - /// Default: true - @objc public var maskAllTextInputs: Bool = true - - /// Enable masking of all images to a placeholder - /// Default: true - @objc public var maskAllImages: Bool = true - - /// Enable masking of all sandboxed system views - /// These may include UIImagePickerController, PHPickerViewController and CNContactPickerViewController - /// Default: true - @objc public var maskAllSandboxedViews: Bool = true - - /// Enable masking of images that likely originated from user's photo library (UIKit only) - /// Default: false - /// - /// - Note: Deprecated - @available(*, deprecated, message: "This property has no effect and will be removed in the next major release. To learn how to manually mask user photos please see our Privacy controls documentation: https://posthog.com/docs/session-replay/privacy?tab=iOS") - @objc public var maskPhotoLibraryImages: Bool = false - - /// Enable capturing network telemetry - /// Default: true - @objc public var captureNetworkTelemetry: Bool = true - - /// By default Session replay will capture all the views on the screen as a wireframe, - /// By enabling this option, PostHog will capture the screenshot of the screen. - /// The screenshot may contain sensitive information, use with caution. - /// Default: false - @objc public var screenshotMode: Bool = false - - /// Debouncer delay used to reduce the number of snapshots captured and reduce performance impact - /// This is used for capturing the view as a wireframe or screenshot - /// The lower the number more snapshots will be captured but higher the performance impact - /// Defaults to 1s - @available(*, deprecated, message: "Deprecated in favor of 'throttleDelay' which provides identical functionality. Will be removed in the next major release.") - @objc public var debouncerDelay: TimeInterval { - get { throttleDelay } - set { throttleDelay = newValue } - } - - /// Throttle delay used to reduce the number of snapshots captured and reduce performance impact - /// This is used for capturing the view as a wireframe or screenshot - /// The lower the number more snapshots will be captured but higher the performance impact - /// Defaults to 1s - /// - /// Note: Previously `debouncerDelay` - @objc public var throttleDelay: TimeInterval = 1 - - /// Enable capturing console output for session replay. - /// - /// When enabled, logs from the following sources will be captured: - /// - Standard output (stdout) - /// - Standard error (stderr) - /// - OSLog messages - /// - NSLog messages - /// - /// Each log entry will be tagged with a level (info/warning/error) based on the message content - /// and the source. - /// - /// Defaults to `false` - @objc public var captureLogs: Bool = false - - /// Further configuration for capturing console output - @objc public var captureLogsConfig: PostHogSessionReplayConsoleLogConfig = .init() - - // TODO: sessionRecording config such as networkPayloadCapture, sampleRate, etc - - /// Returns an array of plugins to be installed based on current configuration - func getPlugins() -> [PostHogSessionReplayPlugin] { - var plugins: [PostHogSessionReplayPlugin] = [] - - if captureLogs { - plugins.append(PostHogSessionReplayConsoleLogsPlugin()) - } - - if captureNetworkTelemetry { - plugins.append(PostHogSessionReplayNetworkPlugin()) - } - - return plugins - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/PostHogSessionReplayConsoleLogConfig.swift b/Pods/PostHog/PostHog/Replay/PostHogSessionReplayConsoleLogConfig.swift deleted file mode 100644 index fb9908b..0000000 --- a/Pods/PostHog/PostHog/Replay/PostHogSessionReplayConsoleLogConfig.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// PostHogSessionReplayConsoleLogConfig.swift -// PostHog -// -// Created by Ioannis Josephides on 09/05/2025. -// - -#if os(iOS) - import Foundation - - @objc public class PostHogSessionReplayConsoleLogConfig: NSObject { - /// Block to process and format captured console output for session replay. - /// - /// This block is called whenever console output is captured. It allows you to: - /// 1. Filter or modify log messages before they are sent to session replay - /// 2. Determine the appropriate log level (info/warn/error) for each message - /// 3. Format, sanitize or skip a log messages (e.g. remove sensitive data or PII) - /// - /// The default implementation: - /// - Detect log level (best effort) - /// - Process OSLog messages to remove metadata - /// - /// - Parameter output: The raw console output to process - /// - Returns: Array of `PostHogConsoleLogResult` objects, one for each processed log entry. Return an empty array to skip a log output - @objc public var logSanitizer: ((String) -> PostHogLogEntry?) = PostHogSessionReplayConsoleLogConfig.defaultLogSanitizer - - /// The minimum log level to capture in session replay. - /// Only log messages with this level or higher will be captured. - /// For example, if set to `.warn`: - /// - `.error` messages will be captured - /// - `.warn` messages will be captured - /// - `.info` messages will be skipped - /// - /// Defaults to `.error` to minimize noise in session replays. - @objc public var minLogLevel: PostHogLogLevel = .error - - /// Default implementation for processing console output. - static func defaultLogSanitizer(_ message: String) -> PostHogLogEntry? { - let message = String(message) - // Determine console log level - let level: PostHogLogLevel = { - if message.range(of: logMessageWarningPattern, options: .regularExpression) != nil { return .warn } - if message.range(of: logMessageErrorPattern, options: .regularExpression) != nil { return .error } - return .info - }() - - // For OSLog messages, extract just the log message part - let sanitizedMessage = message.contains("OSLOG-") ? { - if let tabIndex = message.lastIndex(of: "\t") { - return String(message[message.index(after: tabIndex)...]) - } - return message - }() : message - - return PostHogLogEntry(level: level, message: sanitizedMessage) - } - - /// Default regular expression pattern used to identify error-level log messages. - /// - /// By default, it matches common error indicators such as: - /// - The word "error", "exception", "fail" or "failed" - /// - OSLog messages with type "Error" or "Fault" - private static let logMessageErrorPattern = "(error|exception|fail(ed)?|OSLOG-.*type:\"Error\"|OSLOG-.*type:\"Fault\")" - - /// Default regular expression pattern used to identify warning-level log messages. - /// - /// By default, it matches common warning indicators such as: - /// - The words "warning", "warn", "caution", or "deprecated" - /// - OSLog messages with type "Warning" - /// - private static let logMessageWarningPattern = "(warn(ing)?|caution|deprecated|OSLOG-.*type:\"Warning\")" - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/RRStyle.swift b/Pods/PostHog/PostHog/Replay/RRStyle.swift deleted file mode 100644 index 43624fd..0000000 --- a/Pods/PostHog/PostHog/Replay/RRStyle.swift +++ /dev/null @@ -1,96 +0,0 @@ -// swiftlint:disable cyclomatic_complexity - -// -// RRStyle.swift -// PostHog -// -// Created by Manoel Aranda Neto on 21.03.24. -// - -import Foundation - -class RRStyle { - var color: String? - var backgroundColor: String? - var backgroundImage: String? - var borderWidth: Int? - var borderRadius: Int? - var borderColor: String? - var fontSize: Int? - var fontFamily: String? - var horizontalAlign: String? - var verticalAlign: String? - var paddingTop: Int? - var paddingBottom: Int? - var paddingLeft: Int? - var paddingRight: Int? - var bar: String? - - func toDict() -> [String: Any] { - var dict: [String: Any] = [:] - - if let color = color { - dict["color"] = color - } - - if let backgroundColor = backgroundColor { - dict["backgroundColor"] = backgroundColor - } - - if let backgroundImage = backgroundImage { - dict["backgroundImage"] = backgroundImage - } - - if let borderWidth = borderWidth { - dict["borderWidth"] = borderWidth - } - - if let borderRadius = borderRadius { - dict["borderRadius"] = borderRadius - } - - if let borderColor = borderColor { - dict["borderColor"] = borderColor - } - - if let fontSize = fontSize { - dict["fontSize"] = fontSize - } - - if let fontFamily = fontFamily { - dict["fontFamily"] = fontFamily - } - - if let horizontalAlign = horizontalAlign { - dict["horizontalAlign"] = horizontalAlign - } - - if let verticalAlign = verticalAlign { - dict["verticalAlign"] = verticalAlign - } - - if let paddingTop = paddingTop { - dict["paddingTop"] = paddingTop - } - - if let paddingBottom = paddingBottom { - dict["paddingBottom"] = paddingBottom - } - - if let paddingLeft = paddingLeft { - dict["paddingLeft"] = paddingLeft - } - - if let paddingRight = paddingRight { - dict["paddingRight"] = paddingRight - } - - if let bar = bar { - dict["bar"] = bar - } - - return dict - } -} - -// swiftlint:enable cyclomatic_complexity diff --git a/Pods/PostHog/PostHog/Replay/RRWireframe.swift b/Pods/PostHog/PostHog/Replay/RRWireframe.swift deleted file mode 100644 index bd6553a..0000000 --- a/Pods/PostHog/PostHog/Replay/RRWireframe.swift +++ /dev/null @@ -1,133 +0,0 @@ -// swiftlint:disable cyclomatic_complexity - -// -// RRWireframe.swift -// PostHog -// -// Created by Manoel Aranda Neto on 21.03.24. -// - -import Foundation -#if os(iOS) - import UIKit -#endif - -class RRWireframe { - var id: Int = 0 - var posX: Int = 0 - var posY: Int = 0 - var width: Int = 0 - var height: Int = 0 - var childWireframes: [RRWireframe]? - var type: String? // text|image|rectangle|input|div|screenshot - var inputType: String? - var text: String? - var label: String? - var value: Any? // string or number - #if os(iOS) - var image: UIImage? - var maskableWidgets: [CGRect]? - #endif - var base64: String? - var style: RRStyle? - var disabled: Bool? - var checked: Bool? - var options: [String]? - var max: Int? - // internal - var parentId: Int? - - #if os(iOS) - private func maskImage() -> UIImage? { - if let image = image { - // the scale also affects the image size/resolution, from usually 100kb to 15kb each - let redactedImage = UIGraphicsImageRenderer(size: image.size, format: .init(for: .init(displayScale: 1))).image { context in - context.cgContext.interpolationQuality = .none - image.draw(at: .zero) - - if let maskableWidgets = maskableWidgets { - for rect in maskableWidgets { - let path = UIBezierPath(roundedRect: rect, cornerRadius: 10) - UIColor.black.setFill() - path.fill() - } - } - } - return redactedImage - } - return nil - } - #endif - - func toDict() -> [String: Any] { - var dict: [String: Any] = [ - "id": id, - "x": posX, - "y": posY, - "width": width, - "height": height, - ] - - if let childWireframes = childWireframes { - dict["childWireframes"] = childWireframes.map { $0.toDict() } - } - - if let type = type { - dict["type"] = type - } - - if let inputType = inputType { - dict["inputType"] = inputType - } - - if let text = text { - dict["text"] = text - } - - if let label = label { - dict["label"] = label - } - - if let value = value { - dict["value"] = value - } - - #if os(iOS) - if let image = image { - if let maskedImage = maskImage() { - base64 = maskedImage.toBase64() - } else { - base64 = image.toBase64() - } - } - #endif - - if let base64 = base64 { - dict["base64"] = base64 - } - - if let style = style { - dict["style"] = style.toDict() - } - - if let disabled = disabled { - dict["disabled"] = disabled - } - - if let checked = checked { - dict["checked"] = checked - } - - if let options = options { - dict["options"] = options - } - - if let max = max { - dict["max"] = max - } - - return dict - } -} - -// swiftlint:enable cyclomatic_complexity diff --git a/Pods/PostHog/PostHog/Replay/String+Util.swift b/Pods/PostHog/PostHog/Replay/String+Util.swift deleted file mode 100644 index ea8d1fe..0000000 --- a/Pods/PostHog/PostHog/Replay/String+Util.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// String+Util.swift -// PostHog -// -// Created by Manoel Aranda Neto on 21.03.24. -// - -import Foundation - -extension String { - func mask() -> String { - String(repeating: "*", count: count) - } -} diff --git a/Pods/PostHog/PostHog/Replay/UIColor+Util.swift b/Pods/PostHog/PostHog/Replay/UIColor+Util.swift deleted file mode 100644 index b3d0463..0000000 --- a/Pods/PostHog/PostHog/Replay/UIColor+Util.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// UIColor+Util.swift -// PostHog -// -// Created by Manoel Aranda Neto on 21.03.24. -// -#if os(iOS) - - import Foundation - import UIKit - - extension UIColor { - func toRGBString() -> String? { - cgColor.toRGBString() - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/UIImage+Util.swift b/Pods/PostHog/PostHog/Replay/UIImage+Util.swift deleted file mode 100644 index cd067a7..0000000 --- a/Pods/PostHog/PostHog/Replay/UIImage+Util.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// UIImage+Util.swift -// PostHog -// -// Created by Manoel Aranda Neto on 27.11.24. -// - -#if os(iOS) - import Foundation - import UIKit - - extension UIImage { - func toBase64(_ compressionQuality: CGFloat = 0.3) -> String? { - toWebPBase64(compressionQuality) ?? toJpegBase64(compressionQuality) - } - - private func toWebPBase64(_ compressionQuality: CGFloat) -> String? { - webpData(compressionQuality: compressionQuality).map { data in - "data:image/webp;base64,\(data.base64EncodedString())" - } - } - - private func toJpegBase64(_ compressionQuality: CGFloat) -> String? { - jpegData(compressionQuality: compressionQuality).map { data in - "data:image/jpeg;base64,\(data.base64EncodedString())" - } - } - } - - public func imageToBase64(_ image: UIImage, _ compressionQuality: CGFloat = 0.3) -> String? { - image.toBase64(compressionQuality) - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/UITextInputTraits+Util.swift b/Pods/PostHog/PostHog/Replay/UITextInputTraits+Util.swift deleted file mode 100644 index ce54821..0000000 --- a/Pods/PostHog/PostHog/Replay/UITextInputTraits+Util.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// UITextInputTraits+Util.swift -// PostHog -// -// Created by Manoel Aranda Neto on 21.03.24. -// - -#if os(iOS) - import Foundation - import UIKit - - private let sensibleTypes: [UITextContentType] = [ - .newPassword, .oneTimeCode, .creditCardNumber, - .telephoneNumber, .emailAddress, .password, - .username, .URL, .name, .nickname, - .middleName, .familyName, .nameSuffix, - .namePrefix, .organizationName, .location, - .fullStreetAddress, .streetAddressLine1, - .streetAddressLine2, .addressCity, .addressState, - .addressCityAndState, .postalCode, - ] - - extension UITextInputTraits { - func isSensitiveText() -> Bool { - if isSecureTextEntry ?? false { - return true - } - - if let contentType = textContentType, let contentType = contentType { - return sensibleTypes.contains(contentType) - } - - return false - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/UIView+Util.swift b/Pods/PostHog/PostHog/Replay/UIView+Util.swift deleted file mode 100644 index 6818996..0000000 --- a/Pods/PostHog/PostHog/Replay/UIView+Util.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// UIView+Util.swift -// PostHog -// -// Created by Manoel Aranda Neto on 21.03.24. -// - -#if os(iOS) - import Foundation - import UIKit - - extension UIView { - func isVisible() -> Bool { - if isHidden || alpha == 0 || frame == .zero { - return false - } - return true - } - - func isNoCapture() -> Bool { - var isNoCapture = false - if let identifier = accessibilityIdentifier { - isNoCapture = checkLabel(identifier) - } - // read accessibilityLabel from the parent's view to skip the RCTRecursiveAccessibilityLabel on RN which is slow and may cause an endless loop - // see https://github.com/facebook/react-native/issues/33084 - if let label = super.accessibilityLabel, !isNoCapture { - isNoCapture = checkLabel(label) - } - - return isNoCapture - } - - private func checkLabel(_ label: String) -> Bool { - label.lowercased().contains("ph-no-capture") - } - - func toImage() -> UIImage? { - // Avoid Rendering Offscreen Views - let bounds = superview?.bounds ?? bounds - let size = bounds.intersection(bounds).size - - if !size.hasSize() { - return nil - } - - let rendererFormat = UIGraphicsImageRendererFormat.default() - - // This can significantly improve rendering performance because the renderer won't need to - // process transparency. - rendererFormat.opaque = isOpaque - // Another way to improve rendering performance is to scale the renderer's content. - // rendererFormat.scale = 0.5 - let renderer = UIGraphicsImageRenderer(size: size, format: rendererFormat) - - let image = renderer.image { _ in - /// Note: Always `false` for `afterScreenUpdates` since this will cause the screen to flicker when a sensitive text field is visible on screen - /// This can potentially affect capturing a snapshot during a screen transition but we want the lesser of the two evils here - drawHierarchy(in: bounds, afterScreenUpdates: false) - } - - return image - } - - // you need this because of SwiftUI otherwise the coordinates always zeroed for some reason - func toAbsoluteRect(_ window: UIWindow?) -> CGRect { - convert(bounds, to: window) - } - } -#endif diff --git a/Pods/PostHog/PostHog/Replay/ViewTreeSnapshotStatus.swift b/Pods/PostHog/PostHog/Replay/ViewTreeSnapshotStatus.swift deleted file mode 100644 index 2806dc2..0000000 --- a/Pods/PostHog/PostHog/Replay/ViewTreeSnapshotStatus.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// ViewTreeSnapshotStatus.swift -// PostHog -// -// Created by Manoel Aranda Neto on 20.03.24. -// - -import Foundation - -class ViewTreeSnapshotStatus { - var sentFullSnapshot: Bool = false - var sentMetaEvent: Bool = false - var keyboardVisible: Bool = false - var lastSnapshot: Bool = false -} diff --git a/Pods/PostHog/PostHog/Resources/PrivacyInfo.xcprivacy b/Pods/PostHog/PostHog/Resources/PrivacyInfo.xcprivacy deleted file mode 100644 index 8379bfd..0000000 --- a/Pods/PostHog/PostHog/Resources/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,44 +0,0 @@ - - - - - NSPrivacyCollectedDataTypes - - - NSPrivacyCollectedDataType - NSPrivacyCollectedDataTypeProductInteraction - NSPrivacyCollectedDataTypeLinked - - NSPrivacyCollectedDataTypeTracking - - NSPrivacyCollectedDataTypePurposes - - NSPrivacyCollectedDataTypePurposeAnalytics - - - - NSPrivacyCollectedDataType - NSPrivacyCollectedDataTypeOtherUsageData - NSPrivacyCollectedDataTypeLinked - - NSPrivacyCollectedDataTypeTracking - - NSPrivacyCollectedDataTypePurposes - - NSPrivacyCollectedDataTypePurposeAnalytics - - - - NSPrivacyAccessedAPITypes - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults - NSPrivacyAccessedAPITypeReasons - - CA92.1 - - - - - diff --git a/Pods/PostHog/PostHog/Screen Views/ApplicationScreenViewPublisher.swift b/Pods/PostHog/PostHog/Screen Views/ApplicationScreenViewPublisher.swift deleted file mode 100644 index dcb59dc..0000000 --- a/Pods/PostHog/PostHog/Screen Views/ApplicationScreenViewPublisher.swift +++ /dev/null @@ -1,172 +0,0 @@ -// -// ApplicationScreenViewPublisher.swift -// PostHog -// -// Created by Ioannis Josephides on 20/02/2025. -// - -import Foundation - -#if os(iOS) || os(tvOS) - import UIKit -#endif - -typealias ScreenViewHandler = (String) -> Void - -protocol ScreenViewPublishing: AnyObject { - /// Registers a callback for a view appeared event - func onScreenView(_ callback: @escaping ScreenViewHandler) -> RegistrationToken -} - -final class ApplicationScreenViewPublisher: BaseScreenViewPublisher { - static let shared = ApplicationScreenViewPublisher() - - private var hasSwizzled: Bool = false - - func start() { - // no-op if not UIKit - #if os(iOS) || os(tvOS) - swizzleViewDidAppear() - #endif - } - - func stop() { - // no-op if not UIKit - #if os(iOS) || os(tvOS) - unswizzleViewDidAppear() - #endif - } - - override func onScreenView(_ callback: @escaping ScreenViewHandler) -> RegistrationToken { - let id = UUID() - registrationLock.withLock { - self.onScreenViewCallbacks[id] = callback - } - - // start on first callback registration - if !hasSwizzled { - start() - } - - return RegistrationToken { [weak self] in - // Registration token deallocated here - guard let self else { return } - let handlerCount = self.registrationLock.withLock { - self.onScreenViewCallbacks[id] = nil - return self.onScreenViewCallbacks.values.count - } - // stop when there are no more callbacks - if handlerCount <= 0 { - stop() - } - } - } - - #if os(iOS) || os(tvOS) - func swizzleViewDidAppear() { - guard !hasSwizzled else { return } - hasSwizzled = true - swizzle( - forClass: UIViewController.self, - original: #selector(UIViewController.viewDidAppear(_:)), - new: #selector(UIViewController.viewDidAppearOverride) - ) - } - - func unswizzleViewDidAppear() { - guard hasSwizzled else { return } - hasSwizzled = false - swizzle( - forClass: UIViewController.self, - original: #selector(UIViewController.viewDidAppearOverride), - new: #selector(UIViewController.viewDidAppear(_:)) - ) - } - - // Called from swizzled `viewDidAppearOverride` - fileprivate func viewDidAppear(in viewController: UIViewController?) { - // ignore views from keyboard window - guard let window = viewController?.viewIfLoaded?.window, !window.isKeyboardWindow else { - return - } - - guard let top = findVisibleViewController(viewController) else { return } - - if let name = UIViewController.getViewControllerName(top) { - notifyHandlers(screen: name) - } - } - - private func findVisibleViewController(_ controller: UIViewController?) -> UIViewController? { - if let navigationController = controller as? UINavigationController { - return findVisibleViewController(navigationController.visibleViewController) - } - if let tabController = controller as? UITabBarController { - if let selected = tabController.selectedViewController { - return findVisibleViewController(selected) - } - } - if let presented = controller?.presentedViewController { - return findVisibleViewController(presented) - } - return controller - } - #endif -} - -class BaseScreenViewPublisher: ScreenViewPublishing { - fileprivate let registrationLock = NSLock() - - var onScreenViewCallbacks: [UUID: ScreenViewHandler] = [:] - - func onScreenView(_ callback: @escaping ScreenViewHandler) -> RegistrationToken { - let id = UUID() - registrationLock.withLock { - self.onScreenViewCallbacks[id] = callback - } - - return RegistrationToken { [weak self] in - // Registration token deallocated here - guard let self else { return } - self.registrationLock.withLock { - self.onScreenViewCallbacks[id] = nil - } - } - } - - func notifyHandlers(screen: String) { - let handlers = registrationLock.withLock { onScreenViewCallbacks.values } - for handler in handlers { - notifyHander(handler, screen: screen) - } - } - - private func notifyHander(_ handler: @escaping ScreenViewHandler, screen: String) { - if Thread.isMainThread { - handler(screen) - } else { - DispatchQueue.main.async { handler(screen) } - } - } -} - -#if os(iOS) || os(tvOS) - private extension UIViewController { - @objc func viewDidAppearOverride(animated: Bool) { - ApplicationScreenViewPublisher.shared.viewDidAppear(in: activeController) - - // it looks like we're calling ourselves, but we're actually - // calling the original implementation of viewDidAppear since it's been swizzled. - viewDidAppearOverride(animated: animated) - } - - private var activeController: UIViewController? { - // if a view is being dismissed, this will return nil - if let root = viewIfLoaded?.window?.rootViewController { - return root - } - // TODO: handle container controllers (see ph_topViewController) - return UIApplication.getCurrentWindow()?.rootViewController - } - } -#endif diff --git a/Pods/PostHog/PostHog/Screen Views/PostHogScreenViewIntegration.swift b/Pods/PostHog/PostHog/Screen Views/PostHogScreenViewIntegration.swift deleted file mode 100644 index 9f5f6a3..0000000 --- a/Pods/PostHog/PostHog/Screen Views/PostHogScreenViewIntegration.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// PostHogScreenViewIntegration.swift -// PostHog -// -// Created by Ioannis Josephides on 20/02/2025. -// - -import Foundation - -final class PostHogScreenViewIntegration: PostHogIntegration { - var requiresSwizzling: Bool { true } - - private static var integrationInstalledLock = NSLock() - private static var integrationInstalled = false - - private weak var postHog: PostHogSDK? - private var screenViewToken: RegistrationToken? - - func install(_ postHog: PostHogSDK) throws { - try PostHogScreenViewIntegration.integrationInstalledLock.withLock { - if PostHogScreenViewIntegration.integrationInstalled { - throw InternalPostHogError(description: "Autocapture integration already installed to another PostHogSDK instance.") - } - PostHogScreenViewIntegration.integrationInstalled = true - } - - self.postHog = postHog - - start() - } - - func uninstall(_ postHog: PostHogSDK) { - // uninstall only for integration instance - if self.postHog === postHog || self.postHog == nil { - stop() - self.postHog = nil - PostHogScreenViewIntegration.integrationInstalledLock.withLock { - PostHogScreenViewIntegration.integrationInstalled = false - } - } - } - - /** - Start capturing screen view events - */ - func start() { - let screenViewPublisher = DI.main.screenViewPublisher - screenViewToken = screenViewPublisher.onScreenView { [weak self] screen in - self?.captureScreenView(screen: screen) - } - } - - /** - Stop capturing screen view events - */ - func stop() { - screenViewToken = nil - } - - private func captureScreenView(screen screenName: String) { - guard let postHog else { return } - - if postHog.config.captureScreenViews { - postHog.screen(screenName) - } else { - hedgeLog("Skipping $screen event - captureScreenViews is disabled in configuration") - } - } -} - -#if TESTING - extension PostHogScreenViewIntegration { - static func clearInstalls() { - integrationInstalledLock.withLock { - integrationInstalled = false - } - } - } -#endif diff --git a/Pods/PostHog/PostHog/Surveys/BottomSection.swift b/Pods/PostHog/PostHog/Surveys/BottomSection.swift deleted file mode 100644 index 646d2fd..0000000 --- a/Pods/PostHog/PostHog/Surveys/BottomSection.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// BottomSection.swift -// PostHog -// -// Created by Ioannis Josephides on 18/03/2025. -// - -#if os(iOS) - import SwiftUI - - @available(iOS 15.0, *) - struct BottomSection: View { - let label: String - let action: () -> Void - - var body: some View { - Button(label, action: action) - .buttonStyle(SurveyButtonStyle()) - .padding(.bottom, 16) - } - } - -#endif diff --git a/Pods/PostHog/PostHog/Surveys/ConfirmationMessage.swift b/Pods/PostHog/PostHog/Surveys/ConfirmationMessage.swift deleted file mode 100644 index 1cdfd45..0000000 --- a/Pods/PostHog/PostHog/Surveys/ConfirmationMessage.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// ConfirmationMessage.swift -// PostHog -// -// Created by Ioannis Josephides on 13/03/2025. -// - -#if os(iOS) - import SwiftUI - - @available(iOS 15.0, *) - struct ConfirmationMessage: View { - @Environment(\.surveyAppearance) private var appearance - - let onClose: () -> Void - - var body: some View { - VStack(spacing: 16) { - Text(appearance.thankYouMessageHeader) - .font(.body.bold()) - .foregroundStyle(foregroundTextColor) - if let description = appearance.thankYouMessageDescription, appearance.thankYouMessageDescriptionContentType == .text { - Text(description) - .font(.body) - .foregroundStyle(foregroundTextColor) - } - - BottomSection(label: appearance.thankYouMessageCloseButtonText, action: onClose) - .padding(.top, 20) - } - } - - private var foregroundTextColor: Color { - appearance.backgroundColor.getContrastingTextColor() - } - } - - @available(iOS 15.0, *) - #Preview { - ConfirmationMessage {} - } -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurvey.swift b/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurvey.swift deleted file mode 100644 index 8f2345f..0000000 --- a/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurvey.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// PostHogDisplaySurvey.swift -// PostHog -// -// Created by Ioannis Josephides on 18/06/2025. -// - -import Foundation - -/// A model representing a PostHog survey to be displayed to users -@objc public class PostHogDisplaySurvey: NSObject, Identifiable { - /// Unique identifier for the survey - public let id: String - /// Name of the survey - public let name: String - /// Array of questions to be presented in the survey - public let questions: [PostHogDisplaySurveyQuestion] - /// Optional appearance configuration for customizing the survey's look and feel - public let appearance: PostHogDisplaySurveyAppearance? - /// Optional date indicating when the survey should start being shown - public let startDate: Date? - /// Optional date indicating when the survey should stop being shown - public let endDate: Date? - - init( - id: String, - name: String, - questions: [PostHogDisplaySurveyQuestion], - appearance: PostHogDisplaySurveyAppearance?, - startDate: Date?, - endDate: Date? - ) { - self.id = id - self.name = name - self.questions = questions - self.appearance = appearance - self.startDate = startDate - self.endDate = endDate - super.init() - } -} - -/// Type of rating display for survey rating questions -@objc public enum PostHogDisplaySurveyRatingType: Int { - /// Display numeric rating options - case number - /// Display emoji rating options - case emoji -} - -/// Content type for text-based survey elements -@objc public enum PostHogDisplaySurveyTextContentType: Int { - /// Content should be rendered as HTML - case html - /// Content should be rendered as plain text - case text -} diff --git a/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurveyAppearance.swift b/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurveyAppearance.swift deleted file mode 100644 index 0af1bd3..0000000 --- a/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurveyAppearance.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// PostHogDisplaySurveyAppearance.swift -// PostHog -// -// Created by Ioannis Josephides on 19/06/2025. -// - -import Foundation - -/// Model that describes the appearance customization of a PostHog survey -@objc public class PostHogDisplaySurveyAppearance: NSObject { - // General - /// Optional font family to use throughout the survey - public let fontFamily: String? - /// Optional background color as web color (e.g. "#FFFFFF" or "white") - public let backgroundColor: String? - /// Optional border color as web color - public let borderColor: String? - - // Submit button - /// Optional background color for the submit button as web color - public let submitButtonColor: String? - /// Optional custom text for the submit button - public let submitButtonText: String? - /// Optional text color for the submit button as web color - public let submitButtonTextColor: String? - - // Text colors - /// Optional color for description text as web color - public let descriptionTextColor: String? - - // Rating buttons - /// Optional color for rating buttons as web color - public let ratingButtonColor: String? - /// Optional color for active/selected rating buttons as web color - public let ratingButtonActiveColor: String? - - // Input - /// Optional placeholder text for input fields - public let placeholder: String? - - // Thank you message - /// Whether to show a thank you message after survey completion - public let displayThankYouMessage: Bool - /// Optional header text for the thank you message - public let thankYouMessageHeader: String? - /// Optional description text for the thank you message - public let thankYouMessageDescription: String? - /// Optional content type for the thank you message description - public let thankYouMessageDescriptionContentType: PostHogDisplaySurveyTextContentType? - /// Optional text for the close button in the thank you message - public let thankYouMessageCloseButtonText: String? - - init( - fontFamily: String?, - backgroundColor: String?, - borderColor: String?, - submitButtonColor: String?, - submitButtonText: String?, - submitButtonTextColor: String?, - descriptionTextColor: String?, - ratingButtonColor: String?, - ratingButtonActiveColor: String?, - placeholder: String?, - displayThankYouMessage: Bool, - thankYouMessageHeader: String?, - thankYouMessageDescription: String?, - thankYouMessageDescriptionContentType: PostHogDisplaySurveyTextContentType?, - thankYouMessageCloseButtonText: String? - ) { - self.fontFamily = fontFamily - self.backgroundColor = backgroundColor - self.borderColor = borderColor - self.submitButtonColor = submitButtonColor - self.submitButtonText = submitButtonText - self.submitButtonTextColor = submitButtonTextColor - self.descriptionTextColor = descriptionTextColor - self.ratingButtonColor = ratingButtonColor - self.ratingButtonActiveColor = ratingButtonActiveColor - self.placeholder = placeholder - self.displayThankYouMessage = displayThankYouMessage - self.thankYouMessageHeader = thankYouMessageHeader - self.thankYouMessageDescription = thankYouMessageDescription - self.thankYouMessageDescriptionContentType = thankYouMessageDescriptionContentType - self.thankYouMessageCloseButtonText = thankYouMessageCloseButtonText - super.init() - } -} diff --git a/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurveyQuestion.swift b/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurveyQuestion.swift deleted file mode 100644 index bd59ed8..0000000 --- a/Pods/PostHog/PostHog/Surveys/Models/PostHogDisplaySurveyQuestion.swift +++ /dev/null @@ -1,150 +0,0 @@ -// -// PostHogDisplaySurveyQuestion.swift -// PostHog -// -// Created by Ioannis Josephides on 19/06/2025. -// - -import Foundation - -/// Base class for all survey question types -@objc public class PostHogDisplaySurveyQuestion: NSObject { - /// The question ID, empty if none - @objc public let id: String - /// The main question text to display - @objc public let question: String - /// Optional additional description or context for the question - @objc public let questionDescription: String? - /// Content type for the question description (HTML or plain text) - @objc public let questionDescriptionContentType: PostHogDisplaySurveyTextContentType - /// Whether the question can be skipped - @objc public let isOptional: Bool - /// Optional custom text for the question's action button - @objc public let buttonText: String? - - init( - id: String, - question: String, - questionDescription: String?, - questionDescriptionContentType: PostHogDisplaySurveyTextContentType?, - isOptional: Bool, - buttonText: String? - ) { - self.id = id - self.question = question - self.questionDescription = questionDescription - self.questionDescriptionContentType = questionDescriptionContentType ?? .text - self.isOptional = isOptional - self.buttonText = buttonText - super.init() - } -} - -/// Represents an open-ended question where users can input free-form text -@objc public class PostHogDisplayOpenQuestion: PostHogDisplaySurveyQuestion { /**/ } - -/// Represents a question with a clickable link -@objc public class PostHogDisplayLinkQuestion: PostHogDisplaySurveyQuestion { - /// The URL that will be opened when the link is clicked - public let link: String? - - init( - id: String, - question: String, - questionDescription: String?, - questionDescriptionContentType: PostHogDisplaySurveyTextContentType?, - isOptional: Bool, - buttonText: String?, - link: String? - ) { - self.link = link - super.init( - id: id, - question: question, - questionDescription: questionDescription, - questionDescriptionContentType: questionDescriptionContentType, - isOptional: isOptional, - buttonText: buttonText - ) - } -} - -/// Represents a rating question where users can select a rating from a scale -@objc public class PostHogDisplayRatingQuestion: PostHogDisplaySurveyQuestion { - /// The type of rating scale (numbers, emoji) - public let ratingType: PostHogDisplaySurveyRatingType - /// The lower bound of the rating scale - public let scaleLowerBound: Int - /// The upper bound of the rating scale - public let scaleUpperBound: Int - /// The label for the lower bound of the rating scale - public let lowerBoundLabel: String - /// The label for the upper bound of the rating scale - public let upperBoundLabel: String - - init( - id: String, - question: String, - questionDescription: String?, - questionDescriptionContentType: PostHogDisplaySurveyTextContentType?, - isOptional: Bool, - buttonText: String?, - ratingType: PostHogDisplaySurveyRatingType, - scaleLowerBound: Int, - scaleUpperBound: Int, - lowerBoundLabel: String, - upperBoundLabel: String - ) { - self.ratingType = ratingType - self.scaleLowerBound = scaleLowerBound - self.scaleUpperBound = scaleUpperBound - self.lowerBoundLabel = lowerBoundLabel - self.upperBoundLabel = upperBoundLabel - super.init( - id: id, - question: question, - questionDescription: questionDescription, - questionDescriptionContentType: questionDescriptionContentType, - isOptional: isOptional, - buttonText: buttonText - ) - } -} - -/// Represents a multiple or single choice question where users can select one or more options -@objc public class PostHogDisplayChoiceQuestion: PostHogDisplaySurveyQuestion { - /// The list of options for the user to choose from - public let choices: [String] - /// Whether the question includes an "other" option for users to input free-form text - public let hasOpenChoice: Bool - /// Whether the options should be shuffled to randomize the order - public let shuffleOptions: Bool - /// Whether the user can select multiple options - public let isMultipleChoice: Bool - - init( - id: String, - question: String, - questionDescription: String?, - questionDescriptionContentType: PostHogDisplaySurveyTextContentType?, - isOptional: Bool, - buttonText: String?, - choices: [String], - hasOpenChoice: Bool, - shuffleOptions: Bool, - isMultipleChoice: Bool - ) { - self.choices = choices - self.hasOpenChoice = hasOpenChoice - self.shuffleOptions = shuffleOptions - self.isMultipleChoice = isMultipleChoice - super.init( - id: id, - question: question, - questionDescription: questionDescription, - questionDescriptionContentType: questionDescriptionContentType, - isOptional: isOptional, - buttonText: buttonText - ) - } -} diff --git a/Pods/PostHog/PostHog/Surveys/Models/PostHogNextSurveyQuestion.swift b/Pods/PostHog/PostHog/Surveys/Models/PostHogNextSurveyQuestion.swift deleted file mode 100644 index 4e09350..0000000 --- a/Pods/PostHog/PostHog/Surveys/Models/PostHogNextSurveyQuestion.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// PostHogNextSurveyQuestion.swift -// PostHog -// -// Created by Ioannis Josephides on 19/06/2025. -// - -import Foundation - -/// A model representing the next state of the survey progression. -@objc public class PostHogNextSurveyQuestion: NSObject { - /// The index of the next question to be displayed (0-based) - public let questionIndex: Int - /// Whether all questions have been answered and the survey is complete - /// Depending on the survey appearance configuration, you may want to show the "Thank you" message or dismiss the survey at this point - public let isSurveyCompleted: Bool - - init(questionIndex: Int, isSurveyCompleted: Bool) { - self.questionIndex = questionIndex - self.isSurveyCompleted = isSurveyCompleted - super.init() - } -} diff --git a/Pods/PostHog/PostHog/Surveys/Models/PostHogSurveyResponse.swift b/Pods/PostHog/PostHog/Surveys/Models/PostHogSurveyResponse.swift deleted file mode 100644 index 567b6c9..0000000 --- a/Pods/PostHog/PostHog/Surveys/Models/PostHogSurveyResponse.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// PostHogSurveyResponse.swift -// PostHog -// -// Created by Ioannis Josephides on 19/06/2025. -// - -import Foundation - -/// A model representing a user's response to a survey question -@objc @objcMembers -public class PostHogSurveyResponse: NSObject { - /// The type of response (link, rating, text, or multiple choice) - public let type: PostHogSurveyResponseType - /// Whether a link was clicked (for link questions) - public let linkClicked: Bool? - /// The numeric rating value (for rating questions) - public let ratingValue: Int? - /// The text response (for open questions) - public let textValue: String? - /// The selected options (for multiple or single choice questions) - public let selectedOptions: [String]? - - private init( - type: PostHogSurveyResponseType, - linkClicked: Bool? = nil, - ratingValue: Int? = nil, - textValue: String? = nil, - multipleChoiceValues: [String]? = nil - ) { - self.type = type - self.linkClicked = linkClicked - self.ratingValue = ratingValue - self.textValue = textValue - selectedOptions = multipleChoiceValues - } - - /// Creates a response for a link question - /// - Parameter clicked: Whether the link was clicked - public static func link(_ clicked: Bool) -> PostHogSurveyResponse { - PostHogSurveyResponse( - type: .link, - linkClicked: clicked, - ratingValue: nil, - textValue: nil, - multipleChoiceValues: nil - ) - } - - /// Creates a response for a rating question - /// - Parameter rating: The selected rating value - public static func rating(_ rating: Int?) -> PostHogSurveyResponse { - PostHogSurveyResponse( - type: .rating, - linkClicked: nil, - ratingValue: rating, - textValue: nil, - multipleChoiceValues: nil - ) - } - - /// Creates a response for an open-ended question - /// - Parameter openEnded: The text response - public static func openEnded(_ openEnded: String?) -> PostHogSurveyResponse { - PostHogSurveyResponse( - type: .openEnded, - linkClicked: nil, - ratingValue: nil, - textValue: openEnded, - multipleChoiceValues: nil - ) - } - - /// Creates a response for a single-choice question - /// - Parameter singleChoice: The selected option - public static func singleChoice(_ singleChoice: String?) -> PostHogSurveyResponse { - PostHogSurveyResponse( - type: .singleChoice, - linkClicked: nil, - ratingValue: nil, - textValue: nil, - multipleChoiceValues: { - if let singleChoice { [singleChoice] } else { nil } - }() - ) - } - - /// Creates a response for a multiple-choice question - /// - Parameter multipleChoice: The selected options - public static func multipleChoice(_ multipleChoice: [String]?) -> PostHogSurveyResponse { - PostHogSurveyResponse( - type: .multipleChoice, - linkClicked: nil, - ratingValue: nil, - textValue: nil, - multipleChoiceValues: multipleChoice - ) - } -} - -@objc public enum PostHogSurveyResponseType: Int { - case link - case rating - case openEnded - case singleChoice - case multipleChoice -} diff --git a/Pods/PostHog/PostHog/Surveys/PostHogSurveyIntegration.swift b/Pods/PostHog/PostHog/Surveys/PostHogSurveyIntegration.swift deleted file mode 100644 index 40b80fd..0000000 --- a/Pods/PostHog/PostHog/Surveys/PostHogSurveyIntegration.swift +++ /dev/null @@ -1,937 +0,0 @@ -// -// PostHogSurveyIntegration.swift -// PostHog -// -// Created by Ioannis Josephides on 20/02/2025. -// - -#if os(iOS) || TESTING - - import Foundation - #if os(iOS) - import UIKit - #endif - - final class PostHogSurveyIntegration: PostHogIntegration { - var requiresSwizzling: Bool { true } - - private static var integrationInstalledLock = NSLock() - private static var integrationInstalled = false - - typealias SurveyCallback = (_ surveys: [PostHogSurvey]) -> Void - - private let kSurveySeenKeyPrefix = "seenSurvey_" - private let kSurveyResponseKey = "$survey_response" - - private var postHog: PostHogSDK? - private var config: PostHogConfig? { postHog?.config } - private var storage: PostHogStorage? { postHog?.storage } - private var remoteConfig: PostHogRemoteConfig? { postHog?.remoteConfig } - - private var allSurveysLock = NSLock() - private var allSurveys: [PostHogSurvey]? - - private var eventsToSurveysLock = NSLock() - private var eventsToSurveys: [String: [String]] = [:] - - private var seenSurveyKeysLock = NSLock() - private var seenSurveyKeys: [AnyHashable: Any]? - - private var eventActivatedSurveysLock = NSLock() - private var eventActivatedSurveys: Set = [] - - private var didBecomeActiveToken: RegistrationToken? - private var didLayoutViewToken: RegistrationToken? - - private var activeSurveyLock = NSLock() - private var activeSurvey: PostHogSurvey? - private var activeSurveyResponses: [String: PostHogSurveyResponse] = [:] // keyed by question identifier - private var activeSurveyCompleted: Bool = false - private var activeSurveyQuestionIndex: Int = 0 - - func install(_ postHog: PostHogSDK) throws { - try PostHogSurveyIntegration.integrationInstalledLock.withLock { - if PostHogSurveyIntegration.integrationInstalled { - throw InternalPostHogError(description: "Replay integration already installed to another PostHogSDK instance.") - } - PostHogSurveyIntegration.integrationInstalled = true - } - - self.postHog = postHog - start() - } - - func uninstall(_ postHog: PostHogSDK) { - if self.postHog === postHog || self.postHog == nil { - stop() - self.postHog = nil - PostHogSurveyIntegration.integrationInstalledLock.withLock { - PostHogSurveyIntegration.integrationInstalled = false - } - } - } - - func start() { - #if os(iOS) - // TODO: listen to screen view events - didLayoutViewToken = DI.main.viewLayoutPublisher.onViewLayout(throttle: 5) { [weak self] in - self?.showNextSurvey() - } - - didBecomeActiveToken = DI.main.appLifecyclePublisher.onDidBecomeActive { [weak self] in - self?.showNextSurvey() - } - #endif - } - - func stop() { - didBecomeActiveToken = nil - didLayoutViewToken = nil - #if os(iOS) - if #available(iOS 15.0, *) { - config?.surveysConfig.surveysDelegate.cleanupSurveys() - } - #endif - } - - /// Get surveys enabled for the current user - func getActiveMatchingSurveys( - forceReload: Bool = false, - callback: @escaping SurveyCallback - ) { - getSurveys(forceReload: forceReload) { [weak self] surveys in - guard let self else { return } - - let matchingSurveys = surveys - .lazy - .filter { // 1. unseen surveys, - !self.getSurveySeen(survey: $0) - } - .filter(\.isActive) // 2. that are active, - .filter { survey in // 3. and match display conditions, - // TODO: Check screen conditions - // TODO: Check event conditions - let deviceTypeCheck = self.doesSurveyDeviceTypesMatch(survey: survey) - return deviceTypeCheck - } - .filter { survey in // 4. and match linked flags - let allKeys: [String?] = [ - [survey.linkedFlagKey], - [survey.targetingFlagKey], - // we check internal targeting flags only if this survey cannot be activated repeatedly - [survey.canActivateRepeatedly ? nil : survey.internalTargetingFlagKey], - survey.featureFlagKeys?.compactMap { kvp in - kvp.key.isEmpty ? nil : kvp.value - } ?? [], - ] - .joined() - .compactMap { $0 } - .filter { !$0.isEmpty } - - // all keys must be enabled - return Set(allKeys) - .allSatisfy(self.isSurveyFeatureFlagEnabled) - } - .filter { survey in // 5. and if event-based, have been activated by that event - survey.hasEvents ? self.isSurveyEventActivated(survey: survey) : true - } - - callback(Array(matchingSurveys)) - } - } - - // TODO: Decouple PostHogSDK and use registration handlers instead - /// Called from PostHogSDK instance when an event is captured - func onEvent(event: String) { - let activatedSurveys = eventsToSurveysLock.withLock { eventsToSurveys[event] } ?? [] - guard !activatedSurveys.isEmpty else { return } - - eventActivatedSurveysLock.withLock { - for survey in activatedSurveys { - eventActivatedSurveys.insert(survey) - } - } - - DispatchQueue.main.async { - self.showNextSurvey() - } - } - - private func getSurveys(forceReload: Bool = false, callback: @escaping SurveyCallback) { - guard let remoteConfig else { - return - } - - guard let config = config, config._surveys else { - hedgeLog("Surveys disabled. Not loading surveys.") - return callback([]) - } - - // mem cache - let allSurveys = allSurveysLock.withLock { self.allSurveys } - - if let allSurveys, !forceReload { - callback(allSurveys) - } else { - // first or force load - getRemoteConfig(remoteConfig, forceReload: forceReload) { [weak self] config in - self?.getFeatureFlags(remoteConfig, forceReload: forceReload) { [weak self] _ in - self?.decodeAndSetSurveys(remoteConfig: config, callback: callback) - } - } - } - } - - private func getRemoteConfig( - _ remoteConfig: PostHogRemoteConfig, - forceReload: Bool = false, - callback: (([String: Any]?) -> Void)? = nil - ) { - let cached = remoteConfig.getRemoteConfig() - if cached == nil || forceReload { - remoteConfig.reloadRemoteConfig(callback: callback) - } else { - callback?(cached) - } - } - - private func getFeatureFlags( - _ remoteConfig: PostHogRemoteConfig, - forceReload: Bool = false, - callback: (([String: Any]?) -> Void)? = nil - ) { - let cached = remoteConfig.getFeatureFlags() - if cached == nil || forceReload { - remoteConfig.reloadFeatureFlags(callback: callback) - } else { - callback?(cached) - } - } - - private func decodeAndSetSurveys(remoteConfig: [String: Any]?, callback: @escaping SurveyCallback) { - let loadedSurveys: [PostHogSurvey] = decodeSurveys(from: remoteConfig ?? [:]) - - let eventMap = loadedSurveys.reduce(into: [String: [String]]()) { result, current in - if let surveyEvents = current.conditions?.events?.values.map(\.name) { - for event in surveyEvents { - result[event, default: []].append(current.id) - } - } - } - - allSurveysLock.withLock { - self.allSurveys = loadedSurveys - } - eventsToSurveysLock.withLock { - self.eventsToSurveys = eventMap - } - - callback(loadedSurveys) - } - - private func decodeSurveys(from remoteConfig: [String: Any]) -> [PostHogSurvey] { - guard let surveysJSON = remoteConfig["surveys"] as? [[String: Any]] else { - // surveys not json, disabled - return [] - } - - do { - let jsonData = try JSONSerialization.data(withJSONObject: surveysJSON) - return try PostHogApi.jsonDecoder.decode([PostHogSurvey].self, from: jsonData) - } catch { - hedgeLog("Error decoding Surveys: \(error)") - return [] - } - } - - private func isSurveyFeatureFlagEnabled(flagKey: String?) -> Bool { - guard let flagKey, let postHog else { - return false - } - - return postHog.isFeatureEnabled(flagKey) - } - - private func canRenderSurvey(survey: PostHogSurvey) -> Bool { - // only render popover surveys for now - survey.type == .popover - } - - /// Shows next survey in queue. No-op if a survey is already being shown - private func showNextSurvey() { - #if os(iOS) - guard #available(iOS 15.0, *) else { - hedgeLog("[Surveys] Surveys can be rendered only on iOS 15+") - return - } - - guard canShowNextSurvey() else { return } - - // Check if there is a new popover surveys to be displayed - getActiveMatchingSurveys { activeSurveys in - if let survey = activeSurveys.first(where: self.canRenderSurvey) { - // set survey as active - self.setActiveSurvey(survey: survey) - - DispatchQueue.main.async { [weak self] in - if let self { - // render the survey - self.postHog?.config.surveysConfig.surveysDelegate.renderSurvey( - survey.toDisplaySurvey(), - onSurveyShown: self.handleSurveyShown, - onSurveyResponse: self.handleSurveyResponse, - onSurveyClosed: self.handleSurveyClosed - ) - } - } - } - } - #endif - } - - /// Returns the computed storage key for a given survey - private func getSurveySeenKey(_ survey: PostHogSurvey) -> String { - let surveySeenKey = "\(kSurveySeenKeyPrefix)\(survey.id)" - if let currentIteration = survey.currentIteration, currentIteration > 0 { - return "\(surveySeenKey)_\(currentIteration)" - } - return surveySeenKey - } - - /// Checks storage for seenSurvey_ key and returns its value - /// - /// Note: if the survey can be repeatedly activated by its events, or if the key is missing, this value will default to false - private func getSurveySeen(survey: PostHogSurvey) -> Bool { - if survey.canActivateRepeatedly { - // if this survey can activate repeatedly, we override this return value - return false - } - - let key = getSurveySeenKey(survey) - let surveysSeen = getSeenSurveyKeys() - let surveySeen = surveysSeen[key] as? Bool ?? false - - return surveySeen - } - - /// Mark a survey as seen - private func setSurveySeen(survey: PostHogSurvey) { - let key = getSurveySeenKey(survey) - let seenKeys = seenSurveyKeysLock.withLock { - seenSurveyKeys?[key] = true - return seenSurveyKeys - } - - storage?.setDictionary(forKey: .surveySeen, contents: seenKeys ?? [:]) - } - - /// Returns survey seen list (and mem-cache from disk if needed) - private func getSeenSurveyKeys() -> [AnyHashable: Any] { - seenSurveyKeysLock.withLock { - if seenSurveyKeys == nil { - seenSurveyKeys = storage?.getDictionary(forKey: .surveySeen) ?? [:] - } - return seenSurveyKeys ?? [:] - } - } - - /// Returns given match type or default value if nil - private func getMatchTypeOrDefault(_ matchType: PostHogSurveyMatchType?) -> PostHogSurveyMatchType { - matchType ?? .iContains - } - - /// Checks if a survey with a device type condition matches the current device type - private func doesSurveyDeviceTypesMatch(survey: PostHogSurvey) -> Bool { - guard - let conditions = survey.conditions, - let deviceTypes = conditions.deviceTypes, deviceTypes.count > 0 - else { - // not device type restrictions, assume true - return true - } - - guard - let deviceType = PostHogContext.deviceType - else { - // if we don't know the current device type, we assume it is not a match - return false - } - - let matchType = getMatchTypeOrDefault(conditions.deviceTypesMatchType) - - return matchType.matches(targets: deviceTypes, value: deviceType) - } - - /// Checks if a survey has been previously activated by an associated event - private func isSurveyEventActivated(survey: PostHogSurvey) -> Bool { - eventActivatedSurveysLock.withLock { - eventActivatedSurveys.contains(survey.id) - } - } - - /// Handle a survey that is shown - private func handleSurveyShown(survey: PostHogDisplaySurvey) { - let activeSurvey = activeSurveyLock.withLock { self.activeSurvey } - - guard let activeSurvey, survey.id == activeSurvey.id else { - hedgeLog("[Surveys] Received a show event for a non-active survey") - return - } - - sendSurveyShownEvent(survey: activeSurvey) - - // clear up event-activated surveys - if activeSurvey.hasEvents { - eventActivatedSurveysLock.withLock { - _ = eventActivatedSurveys.remove(activeSurvey.id) - } - } - } - - /// Handle a survey response - /// Processes a user's response to a survey question and determines the next question to display - /// - Parameters: - /// - survey: The currently displayed survey - /// - index: The index of the current question being answered - /// - response: The user's response to the current question - /// - Returns: The next question to display based on branching logic, or nil if there was an error - private func handleSurveyResponse(survey: PostHogDisplaySurvey, index: Int, response: PostHogSurveyResponse) -> PostHogNextSurveyQuestion? { - let (activeSurvey, activeSurveyQuestionIndex) = activeSurveyLock.withLock { (self.activeSurvey, self.activeSurveyQuestionIndex) } - - guard let activeSurvey, survey.id == activeSurvey.id else { - hedgeLog("[Surveys] Received a response event for a non-active survey") - return nil - } - - // TODO: ideally the handleSurveyResponse should pass the question ID as param but it would break the Flutter SDK for older versions - let questionId: String - if index < survey.questions.count { - let question = survey.questions[index] - questionId = question.id - } else { - // this should not happen, its only for back compatibility - questionId = "" - } - - // 2. Get next step - let nextStep = getNextSurveyStep( - survey: activeSurvey, - questionIndex: activeSurveyQuestionIndex, - response: response - ) - - let (isCompleted, nextIndex) = switch nextStep { - case let .index(nextIndex): (false, nextIndex) - case .end: (true, activeSurveyQuestionIndex) - } - - let nextSurveyQuestion = PostHogNextSurveyQuestion( - questionIndex: nextIndex, - isSurveyCompleted: isCompleted - ) - - // update response, next question index and survey completion - let allResponses = setActiveSurveyResponse(id: questionId, index: index, response: response, nextQuestion: nextSurveyQuestion) - - // send event if needed - // TODO: Partial responses - if isCompleted { - sendSurveySentEvent(survey: activeSurvey, responses: allResponses) - } - - return nextSurveyQuestion - } - - /// Handle a survey dismiss - private func handleSurveyClosed(survey: PostHogDisplaySurvey) { - let (activeSurvey, activeSurveyCompleted) = activeSurveyLock.withLock { (self.activeSurvey, self.activeSurveyCompleted) } - - guard let activeSurvey, survey.id == activeSurvey.id else { - hedgeLog("Received a close event for a non-active survey") - return - } - - // send survey dismissed event if needed - if !activeSurveyCompleted { - sendSurveyDismissedEvent(survey: activeSurvey) - } - - // mark as seen - setSurveySeen(survey: activeSurvey) - - // clear active survey - clearActiveSurvey() - - // show next survey in queue, if any, after a short delay - DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) { - self.showNextSurvey() - } - } - - /// Sends a `survey shown` event to PostHog instance - private func sendSurveyShownEvent(survey: PostHogSurvey) { - sendSurveyEvent( - event: "survey shown", - survey: survey - ) - } - - /// Sends a `survey sent` event to PostHog instance - /// Sends a survey completion event to PostHog with all collected responses - /// - Parameters: - /// - survey: The completed survey - /// - responses: Dictionary of collected responses for each question - private func sendSurveySentEvent(survey: PostHogSurvey, responses: [String: PostHogSurveyResponse]) { - let responsesProperties: [String: Any] = responses.compactMapValues { resp in - switch resp.type { - case .link: resp.linkClicked == true ? "link clicked" : nil - case .multipleChoice: resp.selectedOptions - case .singleChoice: resp.selectedOptions?.first - case .openEnded: resp.textValue - case .rating: resp.ratingValue.map { "\($0)" } - } - } - - let surveyQuestions = survey.questions.enumerated().map { index, question in - let responseKey = question.id.isEmpty ? getOldResponseKey(for: index) : getNewResponseKey(for: question.id) - var questionData: [String: Any] = [ - "id": question.id, - "question": question.question, - ] - - if let response = responsesProperties[responseKey] { - questionData["response"] = response - } - - return questionData - } - - let questionProperties: [String: Any] = [ - "$survey_questions": surveyQuestions, - "$set": [getSurveyInteractionProperty(survey: survey, property: "responded"): true], - ] - - // TODO: Should be doing some validation before sending the event? - - let additionalProperties = questionProperties.merging(responsesProperties, uniquingKeysWith: { _, new in new }) - - sendSurveyEvent( - event: "survey sent", - survey: survey, - additionalProperties: additionalProperties - ) - } - - /// Sends a `survey dismissed` event to PostHog instance - private func sendSurveyDismissedEvent(survey: PostHogSurvey) { - let additionalProperties: [String: Any] = [ - "$set": [ - getSurveyInteractionProperty(survey: survey, property: "dismissed"): true, - ], - ] - - sendSurveyEvent( - event: "survey dismissed", - survey: survey, - additionalProperties: additionalProperties - ) - } - - private func sendSurveyEvent(event: String, survey: PostHogSurvey, additionalProperties: [String: Any] = [:]) { - guard let postHog else { - hedgeLog("[\(event)] event not captured, PostHog instance not found.") - return - } - - var properties = getBaseSurveyEventProperties(for: survey) - properties.merge(additionalProperties) { _, new in new } - - postHog.capture(event, properties: properties) - } - - private func getBaseSurveyEventProperties(for survey: PostHogSurvey) -> [String: Any] { - // TODO: Add session replay screen name - let props: [String: Any?] = [ - "$survey_name": survey.name, - "$survey_id": survey.id, - "$survey_iteration": survey.currentIteration, - "$survey_iteration_start_date": survey.currentIterationStartDate.map(toISO8601String), - ] - return props.compactMapValues { $0 } - } - - private func getSurveyInteractionProperty(survey: PostHogSurvey, property: String) -> String { - var surveyProperty = "$survey_\(property)/\(survey.id)" - - if let currentIteration = survey.currentIteration, currentIteration > 0 { - surveyProperty = "$survey_\(property)/\(survey.id)/\(currentIteration)" - } - - return surveyProperty - } - - private func setActiveSurvey(survey: PostHogSurvey) { - activeSurveyLock.withLock { - if activeSurvey == nil { - activeSurvey = survey - activeSurveyCompleted = false - activeSurveyResponses = [:] - activeSurveyQuestionIndex = 0 - } - } - } - - private func clearActiveSurvey() { - activeSurveyLock.withLock { - activeSurvey = nil - activeSurveyCompleted = false - activeSurveyResponses = [:] - activeSurveyQuestionIndex = 0 - } - } - - /// Stores a response for the current question in the active survey, and returns updated responses - /// - Parameters: - /// - id: The question ID, empty if none - /// - index: The index of the question being answered - /// - response: The user's response to store - /// - nextQuestion: The next question index and completion info - private func setActiveSurveyResponse( - id: String, - index: Int, - response: PostHogSurveyResponse, - nextQuestion: PostHogNextSurveyQuestion - ) -> [String: PostHogSurveyResponse] { - activeSurveyLock.withLock { - // keeping the old response key format for back compatibility - activeSurveyResponses[getOldResponseKey(for: index)] = response - if !id.isEmpty { - // setting the new response key format - activeSurveyResponses[getNewResponseKey(for: id)] = response - } - activeSurveyQuestionIndex = nextQuestion.questionIndex - activeSurveyCompleted = nextQuestion.isSurveyCompleted - return activeSurveyResponses - } - } - - /// Returns next question index - /// - Parameters: - /// - survey: The survey which contains the question - /// - questionIndex: The current question index - /// - response: The current question response - /// - Returns: The next question `.index()` if found, or `.end` survey reach the end - private func getNextSurveyStep( - survey: PostHogSurvey, - questionIndex: Int, - response: PostHogSurveyResponse - ) -> NextSurveyQuestion { - let question = survey.questions[questionIndex] - let nextQuestionIndex = min(questionIndex + 1, survey.questions.count - 1) - - guard let branching = question.branching else { - return questionIndex == survey.questions.count - 1 ? .end : .index(nextQuestionIndex) - } - - switch branching { - case .end: - return .end - - case let .specificQuestion(index): - return .index(min(index, survey.questions.count - 1)) - - case let .responseBased(responseValues): - return getResponseBasedNextQuestionIndex( - survey: survey, - question: question, - response: response, - responseValues: responseValues - ) ?? .index(nextQuestionIndex) - - case .next, .unknown: - return .index(nextQuestionIndex) - } - } - - /// Returns next question index based on response value (from responseValues dictionary) - /// - /// - Parameters: - /// - survey: The survey which contains the question - /// - question: The current question - /// - response: The response to the current question - /// - responseValues: The response values dictionary - /// - Returns: The next index if found in the `responseValues` - private func getResponseBasedNextQuestionIndex( - survey: PostHogSurvey, - question: PostHogSurveyQuestion, - response: PostHogSurveyResponse?, - responseValues: [String: Any] - ) -> NextSurveyQuestion? { - guard let response else { - hedgeLog("[Surveys] Got response based branching, but missing the actual response.") - return nil - } - - switch (question, response.type) { - case let (.singleChoice(singleChoiceQuestion), .singleChoice): - let singleChoiceResponse = response.selectedOptions?.first - var responseIndex = singleChoiceQuestion.choices.firstIndex(of: singleChoiceResponse ?? "") - - if responseIndex == nil, singleChoiceQuestion.hasOpenChoice == true { - // if the response is not found in the choices, it must be the open choice, which is always the last choice - responseIndex = singleChoiceQuestion.choices.count - 1 - } - - if let responseIndex, let nextIndex = responseValues["\(responseIndex)"] { - return processBranchingStep(nextIndex: nextIndex, totalQuestions: survey.questions.count) - } - - hedgeLog("[Surveys] Could not find response index for specific question.") - return nil - - case let (.rating(ratingQuestion), .rating): - if let responseInt = response.ratingValue, - let ratingBucket = getRatingBucketForResponseValue(scale: ratingQuestion.scale, value: responseInt), - let nextIndex = responseValues[ratingBucket] - { - return processBranchingStep(nextIndex: nextIndex, totalQuestions: survey.questions.count) - } - hedgeLog("[Surveys] Could not get response bucket for rating question.") - return nil - - default: - hedgeLog("[Surveys] Got response based branching for an unsupported question type.") - return nil - } - } - - /// Returns next question index based on a branching step result - /// - Parameters: - /// - nextIndex: The next index to process - /// - totalQuestions: The total number of questions in the survey - /// - Returns: The next question index if found, or nil if not - private func processBranchingStep(nextIndex: Any, totalQuestions: Int) -> NextSurveyQuestion? { - if let nextIndex = nextIndex as? Int { - return .index(min(nextIndex, totalQuestions - 1)) - } - if let nextIndex = nextIndex as? String, nextIndex.lowercased() == "end" { - return .end - } - return nil - } - - // Gets the response bucket for a given rating response value, given the scale. - // For example, for a scale of 3, the buckets are "negative", "neutral" and "positive". - private func getRatingBucketForResponseValue(scale: PostHogSurveyRatingScale, value: Int) -> String? { - // swiftlint:disable:previous cyclomatic_complexity - // Validate input ranges - switch scale { - case .threePoint where RatingBucket.threePointRange.contains(value): - switch value { - case BucketThresholds.ThreePoint.negatives: return RatingBucket.negative - case BucketThresholds.ThreePoint.neutrals: return RatingBucket.neutral - default: return RatingBucket.positive - } - - case .fivePoint where RatingBucket.fivePointRange.contains(value): - switch value { - case BucketThresholds.FivePoint.negatives: return RatingBucket.negative - case BucketThresholds.FivePoint.neutrals: return RatingBucket.neutral - default: return RatingBucket.positive - } - - case .sevenPoint where RatingBucket.sevenPointRange.contains(value): - switch value { - case BucketThresholds.SevenPoint.negatives: return RatingBucket.negative - case BucketThresholds.SevenPoint.neutrals: return RatingBucket.neutral - default: return RatingBucket.positive - } - - case .tenPoint where RatingBucket.tenPointRange.contains(value): - switch value { - case BucketThresholds.TenPoint.detractors: return RatingBucket.detractors - case BucketThresholds.TenPoint.passives: return RatingBucket.passives - default: return RatingBucket.promoters - } - - default: - hedgeLog("[Surveys] Cannot get rating bucket for invalid scale: \(scale). The scale must be one of: 3 (1-3), 5 (1-5), 7 (1-7), 10 (0-10).") - return nil - } - } - - // Returns the old survey response key for a specific question index - private func getOldResponseKey(for index: Int) -> String { - index == 0 ? kSurveyResponseKey : "\(kSurveyResponseKey)_\(index)" - } - - // Returns the new survey response key for a specific question id - private func getNewResponseKey(for questionId: String) -> String { - "\(kSurveyResponseKey)_\(questionId)" - } - - func canShowNextSurvey() -> Bool { - activeSurveyLock.withLock { activeSurvey == nil } - } - } - - enum NextSurveyQuestion { - case index(Int) - case end - } - - extension PostHogSurvey: CustomStringConvertible { - var description: String { - "\(name) [\(id)]" - } - } - - extension PostHogSurvey { - var isActive: Bool { - startDate != nil && endDate == nil - } - - var hasEvents: Bool { - conditions?.events?.values.count ?? 0 > 0 - } - - var canActivateRepeatedly: Bool { - conditions?.events?.repeatedActivation == true && hasEvents - } - } - - private extension PostHogSurveyMatchType { - func matches(targets: [String], value: String) -> Bool { - switch self { - // any of the targets contain the value (matched lowercase) - case .iContains: - targets.contains { target in - target.lowercased().contains(value.lowercased()) - } - // *none* of the targets contain the value (matched lowercase) - case .notIContains: - targets.allSatisfy { target in - !target.lowercased().contains(value.lowercased()) - } - // any of the targets match with regex - case .regex: - targets.contains { target in - target.range(of: value, options: .regularExpression) != nil - } - // *none* if the targets match with regex - case .notRegex: - targets.allSatisfy { target in - target.range(of: value, options: .regularExpression) == nil - } - // any of the targets is an exact match - case .exact: - targets.contains { target in - target == value - } - // *none* of the targets is an exact match - case .isNot: - targets.allSatisfy { target in - target != value - } - case .unknown: - false - } - } - } - - private enum RatingBucket { - // Bucket names - static let negative = "negative" - static let neutral = "neutral" - static let positive = "positive" - static let detractors = "detractors" - static let passives = "passives" - static let promoters = "promoters" - - // Scale ranges - static let threePointRange = 1 ... 3 - static let fivePointRange = 1 ... 5 - static let sevenPointRange = 1 ... 7 - static let tenPointRange = 0 ... 10 - } - - private enum BucketThresholds { - enum ThreePoint { - static let negatives = 1 ... 1 - static let neutrals = 2 ... 2 - } - - enum FivePoint { - static let negatives = 1 ... 2 - static let neutrals = 3 ... 3 - } - - enum SevenPoint { - static let negatives = 1 ... 3 - static let neutrals = 4 ... 4 - } - - enum TenPoint { - static let detractors = 0 ... 6 - static let passives = 7 ... 8 - } - } - - #if TESTING - extension PostHogSurveyMatchType { - var matchFunction: (_ targets: [String], _ value: String) -> Bool { - matches - } - } - - extension PostHogSurveyIntegration { - func setSurveys(_ surveys: [PostHogSurvey]) { - allSurveys = surveys - } - - func setShownSurvey(_ survey: PostHogSurvey) { - clearActiveSurvey() - setActiveSurvey(survey: survey) - } - - func getNextQuestion(index: Int, response: PostHogSurveyResponse) -> (Int, Bool)? { - guard let activeSurvey else { return nil } - activeSurveyQuestionIndex = index - if let next = handleSurveyResponse(survey: activeSurvey.toDisplaySurvey(), index: index, response: response) { - return (next.questionIndex, next.isSurveyCompleted) - } - return nil - } - - func testSendSurveyShownEvent(survey: PostHogSurvey) { - sendSurveyShownEvent(survey: survey) - } - - func testSendSurveySentEvent(survey: PostHogSurvey, responses: [String: PostHogSurveyResponse]) { - sendSurveySentEvent(survey: survey, responses: responses) - } - - func testSendSurveyDismissedEvent(survey: PostHogSurvey) { - sendSurveyDismissedEvent(survey: survey) - } - - func testGetBaseSurveyEventProperties(for survey: PostHogSurvey) -> [String: Any] { - getBaseSurveyEventProperties(for: survey) - } - - func testGetSurveyInteractionProperty(survey: PostHogSurvey, property: String) -> String { - getSurveyInteractionProperty(survey: survey, property: property) - } - - func testGetResponseKey(questionId: String) -> String { - getNewResponseKey(for: questionId) - } - - static func clearInstalls() { - integrationInstalledLock.withLock { - integrationInstalled = false - } - } - } - #endif -#endif diff --git a/Pods/PostHog/PostHog/Surveys/PostHogSurveysConfig.swift b/Pods/PostHog/PostHog/Surveys/PostHogSurveysConfig.swift deleted file mode 100644 index 776f2ec..0000000 --- a/Pods/PostHog/PostHog/Surveys/PostHogSurveysConfig.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// PostHogSurveysConfig.swift -// PostHog -// -// Created by Ioannis Josephides on 24/04/2025. -// - -import Foundation - -@objc public class PostHogSurveysConfig: NSObject { - /// Delegate responsible for managing survey presentation in your app. - /// Handles survey rendering, response collection, and lifecycle events. - /// You can provide your own delegate for a custom survey presentation. - /// - /// Defaults to `PostHogSurveysDefaultDelegate` which provides a standard survey UI. - public var surveysDelegate: PostHogSurveysDelegate = PostHogSurveysDefaultDelegate() -} - -/// To be called when a survey is successfully shown to the user -/// - Parameter survey: The survey that was displayed -public typealias OnPostHogSurveyShown = (_ survey: PostHogDisplaySurvey) -> Void - -/// To be called when a user responds to a survey question -/// - Parameters: -/// - survey: The current survey being displayed -/// - index: The index of the question being answered -/// - response: The user's response to the question -/// - Returns: The next question state (next question index and completion flag) -public typealias OnPostHogSurveyResponse = (_ survey: PostHogDisplaySurvey, _ index: Int, _ response: PostHogSurveyResponse) -> PostHogNextSurveyQuestion? - -/// To be called when a survey is dismissed -/// - Parameter survey: The survey that was closed -public typealias OnPostHogSurveyClosed = (_ survey: PostHogDisplaySurvey) -> Void - -@objc public protocol PostHogSurveysDelegate { - /// Called when an activated PostHog survey needs to be rendered on the app's UI - /// - /// - Parameters: - /// - survey: The survey to be displayed to the user - /// - onSurveyShown: To be called when the survey is successfully displayed to the user. - /// - onSurveyResponse: To be called the user submits a response to a question. - /// - onSurveyClosed: To be called when the survey is dismissed - @objc func renderSurvey( - _ survey: PostHogDisplaySurvey, - onSurveyShown: @escaping OnPostHogSurveyShown, - onSurveyResponse: @escaping OnPostHogSurveyResponse, - onSurveyClosed: @escaping OnPostHogSurveyClosed - ) - - /// Called when surveys are stopped to clean up any UI elements and reset the survey display state. - /// This method should handle the dismissal of any active surveys and cleanup of associated resources. - @objc func cleanupSurveys() -} diff --git a/Pods/PostHog/PostHog/Surveys/PostHogSurveysDefaultDelegate.swift b/Pods/PostHog/PostHog/Surveys/PostHogSurveysDefaultDelegate.swift deleted file mode 100644 index 6c854c5..0000000 --- a/Pods/PostHog/PostHog/Surveys/PostHogSurveysDefaultDelegate.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// PostHogSurveysDefaultDelegate.swift -// PostHog -// -// Created by Ioannis Josephides on 18/06/2025. -// - -#if os(iOS) - import UIKit -#else - import Foundation -#endif - -final class PostHogSurveysDefaultDelegate: PostHogSurveysDelegate { - #if os(iOS) - private var surveysWindow: UIWindow? - private var displayController: SurveyDisplayController? - #endif - - func renderSurvey( - _ survey: PostHogDisplaySurvey, - onSurveyShown: @escaping OnPostHogSurveyShown, - onSurveyResponse: @escaping OnPostHogSurveyResponse, - onSurveyClosed: @escaping OnPostHogSurveyClosed - ) { - #if os(iOS) - guard #available(iOS 15.0, *) else { return } - - if surveysWindow == nil { - // setup window for first-time display - setupWindow() - } - - // Setup handlers - displayController?.onSurveyShown = onSurveyShown - displayController?.onSurveyResponse = onSurveyResponse - displayController?.onSurveyClosed = onSurveyClosed - - // Display survey - displayController?.showSurvey(survey) - #endif - } - - func cleanupSurveys() { - #if os(iOS) - displayController?.dismissSurvey() // dismiss any active surveys - surveysWindow?.rootViewController?.dismiss(animated: true) { - self.surveysWindow?.isHidden = true - self.surveysWindow = nil - self.displayController = nil - } - #endif - } - - #if os(iOS) - @available(iOS 15.0, *) - private func setupWindow() { - if let activeWindow = UIApplication.getCurrentWindow(), let activeScene = activeWindow.windowScene { - let controller = SurveyDisplayController() - displayController = controller - surveysWindow = SurveysWindow( - controller: controller, - scene: activeScene - ) - surveysWindow?.isHidden = false - surveysWindow?.windowLevel = activeWindow.windowLevel + 1 - } - } - #endif -} diff --git a/Pods/PostHog/PostHog/Surveys/QuestionHeader.swift b/Pods/PostHog/PostHog/Surveys/QuestionHeader.swift deleted file mode 100644 index 362d0a7..0000000 --- a/Pods/PostHog/PostHog/Surveys/QuestionHeader.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// QuestionHeader.swift -// PostHog -// -// Created by Ioannis Josephides on 13/03/2025. -// - -#if os(iOS) - import SwiftUI - - @available(iOS 15.0, *) - struct QuestionHeader: View { - @Environment(\.surveyAppearance) private var appearance - - let question: String - let description: String? - let contentType: PostHogDisplaySurveyTextContentType - - var body: some View { - VStack(alignment: .leading, spacing: 8) { - Text(question) - .font(.body.bold()) - .foregroundColor(foregroundTextColor) - .multilineTextAlignment(.leading) - if let description, !description.isEmpty, contentType == .text { - Text(description) - .font(.callout) - .foregroundColor(foregroundTextColor) - .multilineTextAlignment(.leading) - } - } - .padding(.top, 16) - .frame(maxWidth: .infinity, alignment: .leading) - } - - private var foregroundTextColor: Color { - appearance.backgroundColor.getContrastingTextColor() - } - } - - @available(iOS 15.0, *) - #Preview { - QuestionHeader( - question: "What can we do to improve our product?", - description: "Any feedback will be helpful!", - contentType: .text - ) - } -#endif diff --git a/Pods/PostHog/PostHog/Surveys/QuestionTypes.swift b/Pods/PostHog/PostHog/Surveys/QuestionTypes.swift deleted file mode 100644 index b55b059..0000000 --- a/Pods/PostHog/PostHog/Surveys/QuestionTypes.swift +++ /dev/null @@ -1,243 +0,0 @@ -// -// QuestionTypes.swift -// PostHog -// -// Created by Ioannis Josephides on 13/03/2025. -// - -#if os(iOS) - import SwiftUI - - @available(iOS 15.0, *) - struct OpenTextQuestionView: View { - @Environment(\.surveyAppearance) private var appearance - - let question: PostHogDisplayOpenQuestion - let onNextQuestion: (String?) -> Void - - @State private var text: String = "" - - var body: some View { - VStack(spacing: 16) { - QuestionHeader( - question: question.question, - description: question.questionDescription, - contentType: question.questionDescriptionContentType - ) - - TextEditor(text: $text) - .frame(height: 80) - .overlay( - Group { - if text.isEmpty { - Text(appearance.placeholder ?? "Start typing...") - .foregroundColor(.secondary) - .offset(x: 5, y: 8) - } - }, - alignment: .topLeading - ) - .padding(8) - .tint(.black) - .background( - RoundedRectangle(cornerRadius: 6) - .stroke(Color(uiColor: .secondaryLabel), lineWidth: 1) - .background(Color.white) - ) - - BottomSection(label: question.buttonText ?? appearance.submitButtonText) { - let resp = text.trimmingCharacters(in: .whitespaces) - onNextQuestion(resp.isEmpty ? nil : text) - } - .disabled(!canSubmit) - } - } - - private var canSubmit: Bool { - if question.isOptional { return true } - return !text.isEmpty - } - } - - @available(iOS 15.0, *) - struct LinkQuestionView: View { - @Environment(\.surveyAppearance) private var appearance - - let question: PostHogDisplayLinkQuestion - let onNextQuestion: (Bool) -> Void - - var body: some View { - VStack(spacing: 16) { - QuestionHeader( - question: question.question, - description: question.questionDescription, - contentType: question.questionDescriptionContentType - ) - - BottomSection(label: question.buttonText ?? appearance.submitButtonText) { - onNextQuestion(true) - if let link, UIApplication.shared.canOpenURL(link) { - UIApplication.shared.open(link) - } - } - } - } - - private var link: URL? { - if let link = question.link { - return URL(string: link) - } - return nil - } - } - - @available(iOS 15.0, *) - struct RatingQuestionView: View { - @Environment(\.surveyAppearance) private var appearance - - let question: PostHogDisplayRatingQuestion - let onNextQuestion: (Int?) -> Void - @State var rating: Int? - - var body: some View { - VStack(spacing: 16) { - QuestionHeader( - question: question.question, - description: question.questionDescription, - contentType: question.questionDescriptionContentType - ) - - if question.ratingType == .emoji { - EmojiRating( - selectedValue: $rating, - scale: scale, - lowerBoundLabel: question.lowerBoundLabel, - upperBoundLabel: question.upperBoundLabel - ) - } else { - NumberRating( - selectedValue: $rating, - scale: scale, - lowerBoundLabel: question.lowerBoundLabel, - upperBoundLabel: question.upperBoundLabel - ) - } - - BottomSection(label: question.buttonText ?? appearance.submitButtonText) { - onNextQuestion(rating) - } - .disabled(!canSubmit) - } - } - - private var canSubmit: Bool { - if question.isOptional { return true } - return rating != nil - } - - private var scale: PostHogSurveyRatingScale { - PostHogSurveyRatingScale(range: question.scaleLowerBound ... question.scaleUpperBound) - } - } - - @available(iOS 15.0, *) - struct SingleChoiceQuestionView: View { - @Environment(\.surveyAppearance) private var appearance - - let question: PostHogDisplayChoiceQuestion - let onNextQuestion: (String?) -> Void - - @State private var selectedChoices: Set = [] - @State private var openChoiceInput: String = "" - - var body: some View { - VStack(spacing: 16) { - QuestionHeader( - question: question.question, - description: question.questionDescription, - contentType: question.questionDescriptionContentType - ) - - MultipleChoiceOptions( - allowsMultipleSelection: false, - hasOpenChoiceQuestion: question.hasOpenChoice, - options: question.choices, - selectedOptions: $selectedChoices, - openChoiceInput: $openChoiceInput - ) - - BottomSection(label: question.buttonText ?? appearance.submitButtonText) { - let response = selectedChoices.first - let openChoiceInput = openChoiceInput.trimmingCharacters(in: .whitespaces) - onNextQuestion(response == openChoice ? openChoiceInput : response) - } - .disabled(!canSubmit) - } - } - - private var canSubmit: Bool { - if question.isOptional { return true } - return selectedChoices.count == 1 && (hasOpenChoiceSelected ? !openChoiceInput.isEmpty : true) - } - - private var hasOpenChoiceSelected: Bool { - guard let openChoice else { return false } - return selectedChoices.contains(openChoice) - } - - private var openChoice: String? { - guard question.hasOpenChoice == true else { return nil } - return question.choices.last - } - } - - @available(iOS 15.0, *) - struct MultipleChoiceQuestionView: View { - @Environment(\.surveyAppearance) private var appearance - - let question: PostHogDisplayChoiceQuestion - let onNextQuestion: ([String]?) -> Void - - @State private var selectedChoices: Set = [] - @State private var openChoiceInput: String = "" - - var body: some View { - VStack(spacing: 16) { - QuestionHeader( - question: question.question, - description: question.questionDescription, - contentType: question.questionDescriptionContentType - ) - - MultipleChoiceOptions( - allowsMultipleSelection: true, - hasOpenChoiceQuestion: question.hasOpenChoice, - options: question.choices, - selectedOptions: $selectedChoices, - openChoiceInput: $openChoiceInput - ) - - BottomSection(label: question.buttonText ?? appearance.submitButtonText) { - let resp = selectedChoices.map { $0 == openChoice ? openChoiceInput : $0 } - onNextQuestion(resp.isEmpty ? nil : resp) - } - .disabled(!canSubmit) - } - } - - private var canSubmit: Bool { - if question.isOptional { return true } - return !selectedChoices.isEmpty && (hasOpenChoiceSelected ? !openChoiceInput.isEmpty : true) - } - - private var hasOpenChoiceSelected: Bool { - guard let openChoice else { return false } - return selectedChoices.contains(openChoice) - } - - private var openChoice: String? { - guard question.hasOpenChoice == true else { return nil } - return question.choices.last - } - } -#endif diff --git a/Pods/PostHog/PostHog/Surveys/SurveyDisplayController.swift b/Pods/PostHog/PostHog/Surveys/SurveyDisplayController.swift deleted file mode 100644 index fad2804..0000000 --- a/Pods/PostHog/PostHog/Surveys/SurveyDisplayController.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// SurveyDisplayController.swift -// PostHog -// -// Created by Ioannis Josephides on 07/03/2025. -// - -#if os(iOS) || Testing - import SwiftUI - - final class SurveyDisplayController: ObservableObject { - @Published var displayedSurvey: PostHogDisplaySurvey? - @Published var isSurveyCompleted: Bool = false - @Published var currentQuestionIndex: Int = 0 - - var onSurveyShown: OnPostHogSurveyShown? - var onSurveyResponse: OnPostHogSurveyResponse? - var onSurveyClosed: OnPostHogSurveyClosed? - - func showSurvey(_ survey: PostHogDisplaySurvey) { - guard displayedSurvey == nil else { - hedgeLog("[Surveys] Already displaying a survey. Skipping") - return - } - - displayedSurvey = survey - isSurveyCompleted = false - currentQuestionIndex = 0 - onSurveyShown?(survey) - } - - func onNextQuestion(index: Int, response: PostHogSurveyResponse) { - guard let displayedSurvey else { return } - guard let next = onSurveyResponse?(displayedSurvey, index, response) else { return } - - currentQuestionIndex = next.questionIndex - isSurveyCompleted = next.isSurveyCompleted - - // auto-dismiss survey when completed - if isSurveyCompleted, displayedSurvey.appearance?.displayThankYouMessage == false { - dismissSurvey() - } - } - - // User dismissed survey - func dismissSurvey() { - if let survey = displayedSurvey { - onSurveyClosed?(survey) - } - displayedSurvey = nil - isSurveyCompleted = false - currentQuestionIndex = 0 - } - } - -#endif diff --git a/Pods/PostHog/PostHog/Surveys/SurveySheet.swift b/Pods/PostHog/PostHog/Surveys/SurveySheet.swift deleted file mode 100644 index 24a1c7a..0000000 --- a/Pods/PostHog/PostHog/Surveys/SurveySheet.swift +++ /dev/null @@ -1,249 +0,0 @@ -// -// SurveySheet.swift -// PostHog -// -// Created by Ioannis Josephides on 12/03/2025. -// - -#if os(iOS) - - import SwiftUI - - @available(iOS 15, *) - struct SurveySheet: View { - let survey: PostHogDisplaySurvey - let isSurveyCompleted: Bool - let currentQuestionIndex: Int - let onClose: () -> Void - let onNextQuestionClicked: (_ index: Int, _ response: PostHogSurveyResponse) -> Void - - @State private var sheetHeight: CGFloat = .zero - - var body: some View { - surveyContent - .animation(.linear(duration: 0.25), value: currentQuestionIndex) - .readFrame(in: .named("survey-scroll-view")) { frame in - sheetHeight = frame.height - } - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - SurveyDismissButton(action: onClose) - } - } - .surveyBottomSheet(height: sheetHeight) - .environment(\.surveyAppearance, appearance) - } - - @ViewBuilder - private var surveyContent: some View { - if isSurveyCompleted, appearance.displayThankYouMessage { - ConfirmationMessage(onClose: onClose) - } else if let currentQuestion { - switch currentQuestion { - case let currentQuestion as PostHogDisplayOpenQuestion: - OpenTextQuestionView(question: currentQuestion) { resp in - onNextQuestionClicked(currentQuestionIndex, .openEnded(resp)) - } - case let currentQuestion as PostHogDisplayLinkQuestion: - LinkQuestionView(question: currentQuestion) { resp in - onNextQuestionClicked(currentQuestionIndex, .link(resp)) - } - case let currentQuestion as PostHogDisplayRatingQuestion: - RatingQuestionView(question: currentQuestion) { resp in - onNextQuestionClicked(currentQuestionIndex, .rating(resp)) - } - case let currentQuestion as PostHogDisplayChoiceQuestion: - if currentQuestion.isMultipleChoice { - MultipleChoiceQuestionView(question: currentQuestion) { resp in - onNextQuestionClicked(currentQuestionIndex, .multipleChoice(resp)) - } - } else { - SingleChoiceQuestionView(question: currentQuestion) { resp in - onNextQuestionClicked(currentQuestionIndex, .singleChoice(resp)) - } - } - default: - EmptyView() - } - } - } - - private var currentQuestion: PostHogDisplaySurveyQuestion? { - guard currentQuestionIndex <= survey.questions.count - 1 else { - return nil - } - return survey.questions[currentQuestionIndex] - } - - private var appearance: SwiftUISurveyAppearance { - .getAppearanceWithDefaults(survey.appearance) - } - } - - @available(iOS 15, *) - private struct SurveyDismissButton: View { - let action: () -> Void - - var body: some View { - Button(action: action) { - Image(systemName: "xmark") - .font(.body) - .foregroundColor(Color(uiColor: .label)) - } - .buttonStyle(.borderless) - } - } - - extension View { - @available(iOS 15, *) - func surveyBottomSheet(height: CGFloat) -> some View { - modifier( - SurveyBottomSheetWithWithDetents(height: height) - ) - } - } - - @available(iOS 15.0, *) - private struct SurveyBottomSheetWithWithDetents: ViewModifier { - @Environment(\.surveyAppearance) private var appearance - - @State private var sheetHeight: CGFloat = .zero - @State private var safeAreaInsetsTop: CGFloat = .zero - - let height: CGFloat - - func body(content: Content) -> some View { - NavigationView { - scrolledContent(with: content) - .background(appearance.backgroundColor) - .navigationBarTitleDisplayMode(.inline) - .readSafeAreaInsets { insets in - DispatchQueue.main.async { - if safeAreaInsetsTop == .zero { - safeAreaInsetsTop = insets.top - } - } - } - } - .interactiveDismissDisabled() - .background( - SurveyPresentationDetentsRepresentable(detents: sheetDetents) - ) - } - - @ViewBuilder - private func scrolledContent(with content: Content) -> some View { - if #available(iOS 16.4, *) { - ScrollView { - content - .padding(.horizontal, 16) - } - .coordinateSpace(name: "survey-scroll-view") - .scrollBounceBehavior(.basedOnSize) - .scrollDismissesKeyboard(.interactively) - } else { - ScrollView { - content - .padding(.horizontal, 16) - } - .coordinateSpace(name: "survey-scroll-view") - } - } - - private var sheetDetents: [SurveyPresentationDetentsRepresentable.Detent] { - if adjustedSheetHeight >= UIScreen.main.bounds.height { - return [.medium, .large] - } - return [.height(adjustedSheetHeight)] - } - - var adjustedSheetHeight: CGFloat { - height + safeAreaInsetsTop - } - } - - struct SwiftUISurveyAppearance { - var fontFamily: Font - var backgroundColor: Color - var submitButtonColor: Color - var submitButtonText: String - var submitButtonTextColor: Color - var descriptionTextColor: Color - var ratingButtonColor: Color? - var ratingButtonActiveColor: Color? - var displayThankYouMessage: Bool - var thankYouMessageHeader: String - var thankYouMessageDescription: String? - var thankYouMessageDescriptionContentType: PostHogDisplaySurveyTextContentType = .text - var thankYouMessageCloseButtonText: String - var borderColor: Color - var placeholder: String? - } - - @available(iOS 15.0, *) - private struct SurveyAppearanceEnvironmentKey: EnvironmentKey { - static let defaultValue: SwiftUISurveyAppearance = .getAppearanceWithDefaults() - } - - extension EnvironmentValues { - @available(iOS 15.0, *) - var surveyAppearance: SwiftUISurveyAppearance { - get { self[SurveyAppearanceEnvironmentKey.self] } - set { self[SurveyAppearanceEnvironmentKey.self] = newValue } - } - } - - extension SwiftUISurveyAppearance { - @available(iOS 15.0, *) - static func getAppearanceWithDefaults(_ appearance: PostHogDisplaySurveyAppearance? = nil) -> SwiftUISurveyAppearance { - SwiftUISurveyAppearance( - fontFamily: Font.customFont(family: appearance?.fontFamily ?? "") ?? Font.body, - backgroundColor: colorFrom(css: appearance?.backgroundColor, defaultColor: .tertiarySystemBackground), - submitButtonColor: colorFrom(css: appearance?.submitButtonColor, defaultColor: .black), - submitButtonText: appearance?.submitButtonText ?? "Submit", - submitButtonTextColor: colorFrom(css: appearance?.submitButtonTextColor, defaultColor: .white), - descriptionTextColor: colorFrom(css: appearance?.descriptionTextColor, defaultColor: .secondaryLabel), - ratingButtonColor: colorFrom(css: appearance?.ratingButtonColor), - ratingButtonActiveColor: colorFrom(css: appearance?.ratingButtonActiveColor), - displayThankYouMessage: appearance?.displayThankYouMessage ?? true, - thankYouMessageHeader: appearance?.thankYouMessageHeader ?? "Thank you for your feedback!", - thankYouMessageDescriptionContentType: appearance?.thankYouMessageDescriptionContentType ?? .text, - thankYouMessageCloseButtonText: appearance?.thankYouMessageCloseButtonText ?? "Close", - borderColor: colorFrom(css: appearance?.borderColor, defaultColor: .systemFill) - ) - } - - @available(iOS 15.0, *) - private static func colorFrom(css hex: String?, defaultColor: UIColor) -> Color { - hex.map { Color(uiColor: UIColor(hex: $0)) } ?? Color(uiColor: defaultColor) - } - - @available(iOS 15.0, *) - private static func colorFrom(css hex: String?) -> Color? { - hex.map { Color(uiColor: UIColor(hex: $0)) } - } - } - - @available(iOS 16.0, *) - extension PresentationDetent { - /// Same as .large detent but without shrinking the source view - static let almostLarge = Self.custom(AlmostLarge.self) - } - - @available(iOS 16.0, *) - struct AlmostLarge: CustomPresentationDetent { - static func height(in context: Context) -> CGFloat? { - context.maxDetentValue - 0.5 - } - } - - extension Font { - static func customFont(family: String) -> Font? { - if let uiFont = UIFont(name: family, size: UIFont.systemFontSize) { - return Font(uiFont) - } - return nil - } - } - -#endif diff --git a/Pods/PostHog/PostHog/Surveys/SurveysRootView.swift b/Pods/PostHog/PostHog/Surveys/SurveysRootView.swift deleted file mode 100644 index 7fc4a58..0000000 --- a/Pods/PostHog/PostHog/Surveys/SurveysRootView.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// SurveysRootView.swift -// PostHog -// -// Created by Ioannis Josephides on 07/03/2025. -// - -#if os(iOS) - import SwiftUI - - @available(iOS 15.0, *) - struct SurveysRootView: View { - @EnvironmentObject private var displayManager: SurveyDisplayController - - var body: some View { - Color.clear - .allowsHitTesting(false) - .sheet(item: displayBinding) { survey in - SurveySheet( - survey: survey, - isSurveyCompleted: displayManager.isSurveyCompleted, - currentQuestionIndex: displayManager.currentQuestionIndex, - onClose: displayManager.dismissSurvey, - onNextQuestionClicked: displayManager.onNextQuestion - ) - .environment(\.colorScheme, .light) // enforce light theme for now - } - } - - private var displayBinding: Binding { - .init( - get: { - displayManager.displayedSurvey - }, - set: { newValue in - // in case interactive dismiss is allowed - if newValue == nil { - displayManager.dismissSurvey() - } - } - ) - } - } -#endif diff --git a/Pods/PostHog/PostHog/Surveys/SurveysWindow.swift b/Pods/PostHog/PostHog/Surveys/SurveysWindow.swift deleted file mode 100644 index e810ce2..0000000 --- a/Pods/PostHog/PostHog/Surveys/SurveysWindow.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// SurveysWindow.swift -// PostHog -// -// Created by Ioannis Josephides on 06/03/2025. -// - -#if os(iOS) - import SwiftUI - import UIKit - - @available(iOS 15.0, *) - final class SurveysWindow: PassthroughWindow { - init(controller: SurveyDisplayController, scene: UIWindowScene) { - super.init(windowScene: scene) - let rootView = SurveysRootView().environmentObject(controller) - let hostingController = UIHostingController(rootView: rootView) - hostingController.view.backgroundColor = .clear - rootViewController = hostingController - } - - required init?(coder _: NSCoder) { - super.init(frame: .zero) - } - } - - class PassthroughWindow: UIWindow { - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - guard - let hitView = super.hitTest(point, with: event), - let rootView = rootViewController?.view - else { - return nil - } - - // if test comes back as our own view, ignore (this is the passthrough part) - return hitView == rootView ? nil : hitView - } - } - -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/EdgeBorder.swift b/Pods/PostHog/PostHog/Surveys/Utils/EdgeBorder.swift deleted file mode 100644 index d03196c..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/EdgeBorder.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// EdgeBorder.swift -// PostHog -// -// Created by Ioannis Josephides on 22/03/2025. -// - -#if os(iOS) - import SwiftUI - - struct EdgeBorder: Shape { - var lineWidth: CGFloat - var edges: [Edge] - - func path(in rect: CGRect) -> Path { - edges.map { edge -> Path in - switch edge { - case .top: return Path(.init(x: rect.minX, y: rect.minY, width: rect.width, height: lineWidth)) - case .bottom: return Path(.init(x: rect.minX, y: rect.maxY - lineWidth, width: rect.width, height: lineWidth)) - case .leading: return Path(.init(x: rect.minX, y: rect.minY, width: lineWidth, height: rect.height)) - case .trailing: return Path(.init(x: rect.maxX - lineWidth, y: rect.minY, width: lineWidth, height: rect.height)) - } - }.reduce(into: Path()) { $0.addPath($1) } - } - } -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/EmojiRating.swift b/Pods/PostHog/PostHog/Surveys/Utils/EmojiRating.swift deleted file mode 100644 index 337be9b..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/EmojiRating.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// EmojiRating.swift -// PostHog -// -// Created by Ioannis Josephides on 11/03/2025. -// - -#if os(iOS) - import SwiftUI - - @available(iOS 15.0, *) - struct EmojiRating: View { - @Environment(\.surveyAppearance) private var appearance - @Binding var selectedValue: Int? - - let scale: PostHogSurveyRatingScale - let lowerBoundLabel: String - let upperBoundLabel: String - - var body: some View { - VStack { - HStack { - ForEach(scale.range, id: \.self) { value in - Button { - withAnimation(.linear(duration: 0.25)) { - selectedValue = selectedValue == value ? nil : value - } - } label: { - let isSelected = selectedValue == value - emoji(for: value) - .frame(width: 48, height: 48) - .font(.body.bold()) - .foregroundColor(foregroundColor(selected: isSelected)) - - if value != scale.range.upperBound { - Spacer() - } - } - } - } - - HStack(spacing: 0) { - Text(lowerBoundLabel) - .foregroundStyle(appearance.descriptionTextColor) - .frame(alignment: .leading) - Spacer() - Text(upperBoundLabel) - .foregroundStyle(appearance.descriptionTextColor) - .frame(alignment: .trailing) - } - } - } - - // swiftlint:disable:next cyclomatic_complexity - @ViewBuilder private func emoji(for value: Int) -> some View { - switch scale { - case .threePoint: - switch value { - case 1: DissatisfiedEmoji() - case 2: NeutralEmoji() - case 3: SatisfiedEmoji() - default: EmptyView() - } - case .fivePoint: - switch value { - case 1: VeryDissatisfiedEmoji() - case 2: DissatisfiedEmoji() - case 3: NeutralEmoji() - case 4: SatisfiedEmoji() - case 5: VerySatisfiedEmoji() - default: EmptyView() - } - default: EmptyView() - } - } - - private func foregroundColor(selected: Bool) -> Color { - selected ? Color(uiColor: .label) : Color(uiColor: .tertiaryLabel) - } - - private var ratingButtonActiveColor: Color { - appearance.ratingButtonActiveColor ?? .black - } - } - - #if DEBUG - @available(iOS 18.0, *) - private struct TestView: View { - @State var selectedValue: Int? - - var body: some View { - NavigationView { - VStack(spacing: 40) { - EmojiRating( - selectedValue: $selectedValue, - scale: .fivePoint, - lowerBoundLabel: "Unlikely", - upperBoundLabel: "Very likely" - ) - .padding(.horizontal, 20) - } - } - .navigationBarTitle(Text("Emoji Rating")) - .environment(\.surveyAppearance.ratingButtonColor, .green.opacity(0.3)) - .environment(\.surveyAppearance.ratingButtonActiveColor, .green) - .environment(\.surveyAppearance.descriptionTextColor, .orange) - } - } - - @available(iOS 18.0, *) - #Preview { - TestView() - } - #endif -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/MultipleChoiceOptions.swift b/Pods/PostHog/PostHog/Surveys/Utils/MultipleChoiceOptions.swift deleted file mode 100644 index 1eb39ce..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/MultipleChoiceOptions.swift +++ /dev/null @@ -1,160 +0,0 @@ -// -// MultipleChoiceOptions.swift -// PostHog -// -// Created by Ioannis Josephides on 11/03/2025. -// - -#if os(iOS) - import SwiftUI - - @available(iOS 15.0, *) - struct MultipleChoiceOptions: View { - let allowsMultipleSelection: Bool - let hasOpenChoiceQuestion: Bool - let options: [String] - - @Binding var selectedOptions: Set - @Binding var openChoiceInput: String - @State private var textFieldRect: CGRect = .zero - @FocusState private var isTextFieldFocused: Bool - - var body: some View { - VStack { - ForEach(options, id: \.self) { option in - let isSelected = isSelected(option) - - Button { - withAnimation(.linear(duration: 0.15)) { - setSelected(!isSelected, option: option) - } - } label: { - if isOpenChoice(option) { - VStack(alignment: .leading) { - Text("\(option):") - .multilineTextAlignment(.leading) - // Invisible text for calculating TextField placement - Text("text-field-placeholder") - .opacity(0) - .frame(maxWidth: .infinity) - .multilineTextAlignment(.leading) - .readFrame(in: .named("SurveyButton")) { frame in - textFieldRect = frame - } - } - .frame(maxWidth: .infinity, alignment: .leading) - .modifier(SurveyOptionStyle(isChecked: isSelected)) - .coordinateSpace(name: "SurveyButton") - } else { - Text(option) - .modifier(SurveyOptionStyle(isChecked: isSelected)) - .multilineTextAlignment(.leading) - } - } - // text field needs to overlay the Button so it can receive touches first when enabled - .overlay(openChoiceField(option), alignment: .topLeading) - } - } - } - - private func isOpenChoice(_ option: String) -> Bool { - hasOpenChoiceQuestion && options.last == option - } - - private func isSelected(_ option: String) -> Bool { - selectedOptions.contains(option) - } - - private func setSelected(_ selected: Bool, option: String) { - if selected { - if allowsMultipleSelection { - selectedOptions.insert(option) - } else { - selectedOptions = [option] - } - - let isOpenChoice = self.isOpenChoice(option) - // requires a small delay since textfield is enabled/disabled based on `selectedOptions` state update - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - isTextFieldFocused = isOpenChoice - } - } else { - selectedOptions.remove(option) - } - } - - @ViewBuilder - private func openChoiceField(_ option: String) -> some View { - if isOpenChoice(option) { - TextField("", text: $openChoiceInput) - .focused($isTextFieldFocused) - .foregroundColor(isSelected(option) ? Color.black : Color.black.opacity(0.5)) - .frame(maxWidth: .infinity, alignment: .leading) - .frame(maxWidth: textFieldRect.size.width) - .disabled(!isSelected(option)) - .offset( - x: textFieldRect.origin.x, - y: textFieldRect.origin.y - ) - } - } - } - - @available(iOS 15.0, *) - private struct SurveyOptionStyle: ViewModifier { - let isChecked: Bool - - func body(content: Content) -> some View { - HStack(alignment: .center, spacing: 8) { - content - .frame(maxWidth: .infinity, alignment: .leading) - .font(isChecked ? .body.bold() : .body) - .animation(.linear(duration: 0.15), value: isChecked) - - if isChecked { - CheckIcon() - .frame(width: 16, height: 12) - } - } - .contentShape(Rectangle()) - .padding(10) - .frame(minHeight: 48) - .background( - RoundedRectangle(cornerRadius: 4) - .stroke(isChecked ? Color.black : Color.black.opacity(0.5), lineWidth: 1) - ) - .foregroundColor(isChecked ? Color.black : Color.black.opacity(0.5)) - .contentShape(Rectangle()) - } - } - - #if DEBUG - @available(iOS 18.0, *) - private struct TestView: View { - @State var selectedOptions: Set = [] - @State var openChoiceInput = "" - - var body: some View { - MultipleChoiceOptions( - allowsMultipleSelection: true, - hasOpenChoiceQuestion: true, - options: [ - "Tutorials", - "Customer case studies", - "Product announcements", - "Other", - ], - selectedOptions: $selectedOptions, - openChoiceInput: $openChoiceInput - ) - .colorScheme(.dark) - .padding() - } - } - - @available(iOS 18.0, *) - #Preview { - TestView() - } - #endif -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/NumberRating.swift b/Pods/PostHog/PostHog/Surveys/Utils/NumberRating.swift deleted file mode 100644 index 4e9aa76..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/NumberRating.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// NumberRating.swift -// PostHog -// -// Created by Ioannis Josephides on 11/03/2025. -// - -#if os(iOS) - import SwiftUI - - @available(iOS 15.0, *) - struct NumberRating: View { - @Environment(\.surveyAppearance) private var appearance - - @Binding var selectedValue: Int? - let scale: PostHogSurveyRatingScale - let lowerBoundLabel: String - let upperBoundLabel: String - - var body: some View { - VStack { - SegmentedControl( - range: scale.range, - height: 45, - selectedValue: $selectedValue - ) { value, selected in - Text("\(value)") - .font(.body.bold()) - .foregroundColor( - foregroundTextColor(selected: selected) - ) - } separatorView: { value, _ in - if value != scale.range.upperBound { - EdgeBorder(lineWidth: 1, edges: [.trailing]) - .foregroundStyle(appearance.borderColor) - } - } indicatorView: { size in - Rectangle() - .fill(ratingButtonActiveColor) - .frame(height: size.height) - .frame(maxHeight: .infinity, alignment: .bottom) - } - .background(ratingButtonColor) - .clipShape(RoundedRectangle(cornerRadius: 6)) - .overlay( - RoundedRectangle(cornerRadius: 6) - .stroke(appearance.borderColor, lineWidth: 2) - ) - HStack { - Text(lowerBoundLabel) - .font(.callout) - .foregroundColor(appearance.descriptionTextColor) - .frame(alignment: .leading) - Spacer() - Text(upperBoundLabel) - .font(.callout) - .foregroundColor(appearance.descriptionTextColor) - .frame(alignment: .trailing) - } - } - - .padding(2) - } - - private func foregroundTextColor(selected: Bool) -> Color { - backgroundColor(selected: selected) - .getContrastingTextColor() - .opacity(foregroundTextOpacity(selected: selected)) - } - - private func foregroundTextOpacity(selected: Bool) -> Double { - selected ? 1 : 0.5 - } - - private func backgroundColor(selected: Bool) -> Color { - selected ? ratingButtonActiveColor : ratingButtonColor - } - - private var ratingButtonColor: Color { - appearance.ratingButtonColor ?? Color(uiColor: .secondarySystemBackground) - } - - private var ratingButtonActiveColor: Color { - appearance.ratingButtonActiveColor ?? .black - } - } - - #if DEBUG - @available(iOS 18.0, *) - private struct TestView: View { - @State var selectedValue: Int? - - var body: some View { - NavigationView { - VStack(spacing: 15) { - NumberRating( - selectedValue: $selectedValue, - scale: .tenPoint, - lowerBoundLabel: "Unlikely", - upperBoundLabel: "Very Likely" - ) - } - .padding() - } - .navigationBarTitle(Text("Number Rating")) - .environment(\.colorScheme, .light) - } - } - - @available(iOS 18.0, *) - #Preview { - TestView() - } - #endif -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/Resources.swift b/Pods/PostHog/PostHog/Surveys/Utils/Resources.swift deleted file mode 100644 index 8aa59b6..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/Resources.swift +++ /dev/null @@ -1,431 +0,0 @@ -// -// Resources.swift -// PostHog -// -// Created by Ioannis Josephides on 10/03/2025. -// - -// see: https://github.com/bring-shrubbery/SVG-to-SwiftUI - -// swiftlint:disable line_length -#if os(iOS) - import SwiftUI - - struct VeryDissatisfiedEmoji: Shape { - func path(in rect: CGRect) -> Path { - var path = Path() - let width = rect.size.width - let height = rect.size.height - path.move(to: CGPoint(x: 0.5 * width, y: -0.43438 * height)) - path.addQuadCurve(to: CGPoint(x: 0.37344 * width, y: -0.39531 * height), control: CGPoint(x: 0.43021 * width, y: -0.43438 * height)) - path.addQuadCurve(to: CGPoint(x: 0.28958 * width, y: -0.29167 * height), control: CGPoint(x: 0.31667 * width, y: -0.35625 * height)) - path.addLine(to: CGPoint(x: 0.71042 * width, y: -0.29167 * height)) - path.addQuadCurve(to: CGPoint(x: 0.62708 * width, y: -0.39583 * height), control: CGPoint(x: 0.68437 * width, y: -0.35729 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.43438 * height), control: CGPoint(x: 0.56979 * width, y: -0.43438 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.30938 * width, y: -0.50938 * height)) - path.addLine(to: CGPoint(x: 0.36146 * width, y: -0.55625 * height)) - path.addLine(to: CGPoint(x: 0.40833 * width, y: -0.50938 * height)) - path.addLine(to: CGPoint(x: 0.44062 * width, y: -0.54688 * height)) - path.addLine(to: CGPoint(x: 0.39375 * width, y: -0.59375 * height)) - path.addLine(to: CGPoint(x: 0.44062 * width, y: -0.64063 * height)) - path.addLine(to: CGPoint(x: 0.40833 * width, y: -0.67812 * height)) - path.addLine(to: CGPoint(x: 0.36146 * width, y: -0.63125 * height)) - path.addLine(to: CGPoint(x: 0.30938 * width, y: -0.67812 * height)) - path.addLine(to: CGPoint(x: 0.27708 * width, y: -0.64063 * height)) - path.addLine(to: CGPoint(x: 0.32396 * width, y: -0.59375 * height)) - path.addLine(to: CGPoint(x: 0.27708 * width, y: -0.54688 * height)) - path.addLine(to: CGPoint(x: 0.30938 * width, y: -0.50938 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.59271 * width, y: -0.50938 * height)) - path.addLine(to: CGPoint(x: 0.63854 * width, y: -0.55625 * height)) - path.addLine(to: CGPoint(x: 0.69167 * width, y: -0.50938 * height)) - path.addLine(to: CGPoint(x: 0.72396 * width, y: -0.54688 * height)) - path.addLine(to: CGPoint(x: 0.67708 * width, y: -0.59375 * height)) - path.addLine(to: CGPoint(x: 0.72396 * width, y: -0.64063 * height)) - path.addLine(to: CGPoint(x: 0.69167 * width, y: -0.67812 * height)) - path.addLine(to: CGPoint(x: 0.63854 * width, y: -0.63125 * height)) - path.addLine(to: CGPoint(x: 0.59271 * width, y: -0.67812 * height)) - path.addLine(to: CGPoint(x: 0.56042 * width, y: -0.64063 * height)) - path.addLine(to: CGPoint(x: 0.60625 * width, y: -0.59375 * height)) - path.addLine(to: CGPoint(x: 0.56042 * width, y: -0.54688 * height)) - path.addLine(to: CGPoint(x: 0.59271 * width, y: -0.50938 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.11615 * height), control: CGPoint(x: 0.41354 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.20521 * height), control: CGPoint(x: 0.26146 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.3375 * height), control: CGPoint(x: 0.14896 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.08333 * width, y: -0.5 * height), control: CGPoint(x: 0.08333 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.6625 * height), control: CGPoint(x: 0.08333 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.79479 * height), control: CGPoint(x: 0.14896 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.88385 * height), control: CGPoint(x: 0.26146 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.91667 * height), control: CGPoint(x: 0.41354 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.88385 * height), control: CGPoint(x: 0.58646 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.79479 * height), control: CGPoint(x: 0.73854 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.6625 * height), control: CGPoint(x: 0.85104 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.91667 * width, y: -0.5 * height), control: CGPoint(x: 0.91667 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.3375 * height), control: CGPoint(x: 0.91667 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.20521 * height), control: CGPoint(x: 0.85104 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.11615 * height), control: CGPoint(x: 0.73854 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.08333 * height), control: CGPoint(x: 0.58646 * width, y: -0.08333 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.5 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75104 * width, y: -0.24896 * height), control: CGPoint(x: 0.64792 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.85417 * width, y: -0.5 * height), control: CGPoint(x: 0.85417 * width, y: -0.35208 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75104 * width, y: -0.75104 * height), control: CGPoint(x: 0.85417 * width, y: -0.64792 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.85417 * height), control: CGPoint(x: 0.64792 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24896 * width, y: -0.75104 * height), control: CGPoint(x: 0.35208 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.14583 * width, y: -0.5 * height), control: CGPoint(x: 0.14583 * width, y: -0.64792 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24896 * width, y: -0.24896 * height), control: CGPoint(x: 0.14583 * width, y: -0.35208 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.14583 * height), control: CGPoint(x: 0.35208 * width, y: -0.14583 * height)) - path.closeSubpath() - return path.offsetBy(dx: 0, dy: height) - } - } - - struct VerySatisfiedEmoji: Shape { - func path(in rect: CGRect) -> Path { - var path = Path() - let width = rect.size.width - let height = rect.size.height - path.move(to: CGPoint(x: 0.49948 * width, y: -0.27187 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6099 * width, y: -0.29896 * height), control: CGPoint(x: 0.55937 * width, y: -0.27187 * height)) - path.addQuadCurve(to: CGPoint(x: 0.69167 * width, y: -0.37437 * height), control: CGPoint(x: 0.66042 * width, y: -0.32604 * height)) - path.addQuadCurve(to: CGPoint(x: 0.69089 * width, y: -0.39792 * height), control: CGPoint(x: 0.69792 * width, y: -0.38646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.66979 * width, y: -0.40937 * height), control: CGPoint(x: 0.68385 * width, y: -0.40937 * height)) - path.addLine(to: CGPoint(x: 0.33012 * width, y: -0.40937 * height)) - path.addQuadCurve(to: CGPoint(x: 0.30885 * width, y: -0.39792 * height), control: CGPoint(x: 0.31562 * width, y: -0.40937 * height)) - path.addQuadCurve(to: CGPoint(x: 0.30833 * width, y: -0.37437 * height), control: CGPoint(x: 0.30208 * width, y: -0.38646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3901 * width, y: -0.29896 * height), control: CGPoint(x: 0.33958 * width, y: -0.32604 * height)) - path.addQuadCurve(to: CGPoint(x: 0.49948 * width, y: -0.27187 * height), control: CGPoint(x: 0.44062 * width, y: -0.27187 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.36146 * width, y: -0.60208 * height)) - path.addLine(to: CGPoint(x: 0.38958 * width, y: -0.57396 * height)) - path.addQuadCurve(to: CGPoint(x: 0.40814 * width, y: -0.56563 * height), control: CGPoint(x: 0.39754 * width, y: -0.56563 * height)) - path.addQuadCurve(to: CGPoint(x: 0.42708 * width, y: -0.57396 * height), control: CGPoint(x: 0.41875 * width, y: -0.56563 * height)) - path.addQuadCurve(to: CGPoint(x: 0.43542 * width, y: -0.59271 * height), control: CGPoint(x: 0.43542 * width, y: -0.58229 * height)) - path.addQuadCurve(to: CGPoint(x: 0.42708 * width, y: -0.61146 * height), control: CGPoint(x: 0.43542 * width, y: -0.60313 * height)) - path.addLine(to: CGPoint(x: 0.38333 * width, y: -0.65521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.36156 * width, y: -0.66458 * height), control: CGPoint(x: 0.37417 * width, y: -0.66458 * height)) - path.addQuadCurve(to: CGPoint(x: 0.33958 * width, y: -0.65521 * height), control: CGPoint(x: 0.34896 * width, y: -0.66458 * height)) - path.addLine(to: CGPoint(x: 0.29583 * width, y: -0.61146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.2875 * width, y: -0.5929 * height), control: CGPoint(x: 0.2875 * width, y: -0.6035 * height)) - path.addQuadCurve(to: CGPoint(x: 0.29583 * width, y: -0.57396 * height), control: CGPoint(x: 0.2875 * width, y: -0.58229 * height)) - path.addQuadCurve(to: CGPoint(x: 0.31458 * width, y: -0.56563 * height), control: CGPoint(x: 0.30417 * width, y: -0.56563 * height)) - path.addQuadCurve(to: CGPoint(x: 0.33333 * width, y: -0.57396 * height), control: CGPoint(x: 0.325 * width, y: -0.56563 * height)) - path.addLine(to: CGPoint(x: 0.36146 * width, y: -0.60208 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.63958 * width, y: -0.60208 * height)) - path.addLine(to: CGPoint(x: 0.66771 * width, y: -0.57396 * height)) - path.addQuadCurve(to: CGPoint(x: 0.68646 * width, y: -0.56563 * height), control: CGPoint(x: 0.67574 * width, y: -0.56563 * height)) - path.addQuadCurve(to: CGPoint(x: 0.70521 * width, y: -0.57396 * height), control: CGPoint(x: 0.69717 * width, y: -0.56563 * height)) - path.addQuadCurve(to: CGPoint(x: 0.71354 * width, y: -0.59252 * height), control: CGPoint(x: 0.71354 * width, y: -0.58191 * height)) - path.addQuadCurve(to: CGPoint(x: 0.70521 * width, y: -0.61146 * height), control: CGPoint(x: 0.71354 * width, y: -0.60313 * height)) - path.addLine(to: CGPoint(x: 0.66146 * width, y: -0.65521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.63969 * width, y: -0.66458 * height), control: CGPoint(x: 0.65229 * width, y: -0.66458 * height)) - path.addQuadCurve(to: CGPoint(x: 0.61771 * width, y: -0.65521 * height), control: CGPoint(x: 0.62708 * width, y: -0.66458 * height)) - path.addLine(to: CGPoint(x: 0.57396 * width, y: -0.61146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.56563 * width, y: -0.59271 * height), control: CGPoint(x: 0.56563 * width, y: -0.60342 * height)) - path.addQuadCurve(to: CGPoint(x: 0.57396 * width, y: -0.57396 * height), control: CGPoint(x: 0.56563 * width, y: -0.58199 * height)) - path.addQuadCurve(to: CGPoint(x: 0.59252 * width, y: -0.56563 * height), control: CGPoint(x: 0.58191 * width, y: -0.56563 * height)) - path.addQuadCurve(to: CGPoint(x: 0.61146 * width, y: -0.57396 * height), control: CGPoint(x: 0.60313 * width, y: -0.56563 * height)) - path.addLine(to: CGPoint(x: 0.63958 * width, y: -0.60208 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.11615 * height), control: CGPoint(x: 0.41354 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.20521 * height), control: CGPoint(x: 0.26146 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.3375 * height), control: CGPoint(x: 0.14896 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.08333 * width, y: -0.5 * height), control: CGPoint(x: 0.08333 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.6625 * height), control: CGPoint(x: 0.08333 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.79479 * height), control: CGPoint(x: 0.14896 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.88385 * height), control: CGPoint(x: 0.26146 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.91667 * height), control: CGPoint(x: 0.41354 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.88385 * height), control: CGPoint(x: 0.58646 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.79479 * height), control: CGPoint(x: 0.73854 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.6625 * height), control: CGPoint(x: 0.85104 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.91667 * width, y: -0.5 * height), control: CGPoint(x: 0.91667 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.3375 * height), control: CGPoint(x: 0.91667 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.20521 * height), control: CGPoint(x: 0.85104 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.11615 * height), control: CGPoint(x: 0.73854 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.08333 * height), control: CGPoint(x: 0.58646 * width, y: -0.08333 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.5 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75124 * width, y: -0.24876 * height), control: CGPoint(x: 0.64831 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.85417 * width, y: -0.5 * height), control: CGPoint(x: 0.85417 * width, y: -0.35169 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75124 * width, y: -0.75124 * height), control: CGPoint(x: 0.85417 * width, y: -0.64831 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.85417 * height), control: CGPoint(x: 0.64831 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24876 * width, y: -0.75124 * height), control: CGPoint(x: 0.35169 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.14583 * width, y: -0.5 * height), control: CGPoint(x: 0.14583 * width, y: -0.64831 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24876 * width, y: -0.24876 * height), control: CGPoint(x: 0.14583 * width, y: -0.35169 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.14583 * height), control: CGPoint(x: 0.35169 * width, y: -0.14583 * height)) - path.closeSubpath() - return path.offsetBy(dx: 0, dy: height) - } - } - - struct DissatisfiedEmoji: Shape { - func path(in rect: CGRect) -> Path { - var path = Path() - let width = rect.size.width - let height = rect.size.height - path.move(to: CGPoint(x: 0.65208 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.69193 * width, y: -0.57161 * height), control: CGPoint(x: 0.67552 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.70833 * width, y: -0.61146 * height), control: CGPoint(x: 0.70833 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.69193 * width, y: -0.6513 * height), control: CGPoint(x: 0.70833 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.65208 * width, y: -0.66771 * height), control: CGPoint(x: 0.67552 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.61224 * width, y: -0.6513 * height), control: CGPoint(x: 0.62865 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.59583 * width, y: -0.61146 * height), control: CGPoint(x: 0.59583 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.61224 * width, y: -0.57161 * height), control: CGPoint(x: 0.59583 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.65208 * width, y: -0.55521 * height), control: CGPoint(x: 0.62865 * width, y: -0.55521 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.34792 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.38776 * width, y: -0.57161 * height), control: CGPoint(x: 0.37135 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.40417 * width, y: -0.61146 * height), control: CGPoint(x: 0.40417 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.38776 * width, y: -0.6513 * height), control: CGPoint(x: 0.40417 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.34792 * width, y: -0.66771 * height), control: CGPoint(x: 0.37135 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.30807 * width, y: -0.6513 * height), control: CGPoint(x: 0.32448 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.29167 * width, y: -0.61146 * height), control: CGPoint(x: 0.29167 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.30807 * width, y: -0.57161 * height), control: CGPoint(x: 0.29167 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.34792 * width, y: -0.55521 * height), control: CGPoint(x: 0.32448 * width, y: -0.55521 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.50018 * width, y: -0.43438 * height)) - path.addQuadCurve(to: CGPoint(x: 0.37344 * width, y: -0.39531 * height), control: CGPoint(x: 0.43021 * width, y: -0.43438 * height)) - path.addQuadCurve(to: CGPoint(x: 0.28958 * width, y: -0.29167 * height), control: CGPoint(x: 0.31667 * width, y: -0.35625 * height)) - path.addLine(to: CGPoint(x: 0.34479 * width, y: -0.29167 * height)) - path.addQuadCurve(to: CGPoint(x: 0.40956 * width, y: -0.35938 * height), control: CGPoint(x: 0.36771 * width, y: -0.33542 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5007 * width, y: -0.38333 * height), control: CGPoint(x: 0.4514 * width, y: -0.38333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.59115 * width, y: -0.35885 * height), control: CGPoint(x: 0.55 * width, y: -0.38333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.65625 * width, y: -0.29167 * height), control: CGPoint(x: 0.63229 * width, y: -0.33437 * height)) - path.addLine(to: CGPoint(x: 0.71042 * width, y: -0.29167 * height)) - path.addQuadCurve(to: CGPoint(x: 0.62726 * width, y: -0.39583 * height), control: CGPoint(x: 0.68437 * width, y: -0.35729 * height)) - path.addQuadCurve(to: CGPoint(x: 0.50018 * width, y: -0.43438 * height), control: CGPoint(x: 0.57015 * width, y: -0.43438 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.11615 * height), control: CGPoint(x: 0.41354 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.20521 * height), control: CGPoint(x: 0.26146 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.3375 * height), control: CGPoint(x: 0.14896 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.08333 * width, y: -0.5 * height), control: CGPoint(x: 0.08333 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.6625 * height), control: CGPoint(x: 0.08333 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.79479 * height), control: CGPoint(x: 0.14896 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.88385 * height), control: CGPoint(x: 0.26146 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.91667 * height), control: CGPoint(x: 0.41354 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.88385 * height), control: CGPoint(x: 0.58646 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.79479 * height), control: CGPoint(x: 0.73854 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.6625 * height), control: CGPoint(x: 0.85104 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.91667 * width, y: -0.5 * height), control: CGPoint(x: 0.91667 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.3375 * height), control: CGPoint(x: 0.91667 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.20521 * height), control: CGPoint(x: 0.85104 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.11615 * height), control: CGPoint(x: 0.73854 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.08333 * height), control: CGPoint(x: 0.58646 * width, y: -0.08333 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.5 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75124 * width, y: -0.24876 * height), control: CGPoint(x: 0.64831 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.85417 * width, y: -0.5 * height), control: CGPoint(x: 0.85417 * width, y: -0.35169 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75124 * width, y: -0.75124 * height), control: CGPoint(x: 0.85417 * width, y: -0.64831 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.85417 * height), control: CGPoint(x: 0.64831 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24876 * width, y: -0.75124 * height), control: CGPoint(x: 0.35169 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.14583 * width, y: -0.5 * height), control: CGPoint(x: 0.14583 * width, y: -0.64831 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24876 * width, y: -0.24876 * height), control: CGPoint(x: 0.14583 * width, y: -0.35169 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.14583 * height), control: CGPoint(x: 0.35169 * width, y: -0.14583 * height)) - path.closeSubpath() - return path.offsetBy(dx: 0, dy: height) - } - } - - struct NeutralEmoji: Shape { - func path(in rect: CGRect) -> Path { - var path = Path() - let width = rect.size.width - let height = rect.size.height - path.move(to: CGPoint(x: 0.65208 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.69193 * width, y: -0.57161 * height), control: CGPoint(x: 0.67552 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.70833 * width, y: -0.61146 * height), control: CGPoint(x: 0.70833 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.69193 * width, y: -0.6513 * height), control: CGPoint(x: 0.70833 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.65208 * width, y: -0.66771 * height), control: CGPoint(x: 0.67552 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.61224 * width, y: -0.6513 * height), control: CGPoint(x: 0.62865 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.59583 * width, y: -0.61146 * height), control: CGPoint(x: 0.59583 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.61224 * width, y: -0.57161 * height), control: CGPoint(x: 0.59583 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.65208 * width, y: -0.55521 * height), control: CGPoint(x: 0.62865 * width, y: -0.55521 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.34792 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.38776 * width, y: -0.57161 * height), control: CGPoint(x: 0.37135 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.40417 * width, y: -0.61146 * height), control: CGPoint(x: 0.40417 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.38776 * width, y: -0.6513 * height), control: CGPoint(x: 0.40417 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.34792 * width, y: -0.66771 * height), control: CGPoint(x: 0.37135 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.30807 * width, y: -0.6513 * height), control: CGPoint(x: 0.32448 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.29167 * width, y: -0.61146 * height), control: CGPoint(x: 0.29167 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.30807 * width, y: -0.57161 * height), control: CGPoint(x: 0.29167 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.34792 * width, y: -0.55521 * height), control: CGPoint(x: 0.32448 * width, y: -0.55521 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.36875 * width, y: -0.35313 * height)) - path.addLine(to: CGPoint(x: 0.63229 * width, y: -0.35313 * height)) - path.addLine(to: CGPoint(x: 0.63229 * width, y: -0.40417 * height)) - path.addLine(to: CGPoint(x: 0.36875 * width, y: -0.40417 * height)) - path.addLine(to: CGPoint(x: 0.36875 * width, y: -0.35313 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.11615 * height), control: CGPoint(x: 0.41354 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.20521 * height), control: CGPoint(x: 0.26146 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.3375 * height), control: CGPoint(x: 0.14896 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.08333 * width, y: -0.5 * height), control: CGPoint(x: 0.08333 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.6625 * height), control: CGPoint(x: 0.08333 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.79479 * height), control: CGPoint(x: 0.14896 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.88385 * height), control: CGPoint(x: 0.26146 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.91667 * height), control: CGPoint(x: 0.41354 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.88385 * height), control: CGPoint(x: 0.58646 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.79479 * height), control: CGPoint(x: 0.73854 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.6625 * height), control: CGPoint(x: 0.85104 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.91667 * width, y: -0.5 * height), control: CGPoint(x: 0.91667 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.3375 * height), control: CGPoint(x: 0.91667 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.20521 * height), control: CGPoint(x: 0.85104 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.11615 * height), control: CGPoint(x: 0.73854 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.08333 * height), control: CGPoint(x: 0.58646 * width, y: -0.08333 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.5 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75124 * width, y: -0.24876 * height), control: CGPoint(x: 0.64831 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.85417 * width, y: -0.5 * height), control: CGPoint(x: 0.85417 * width, y: -0.35169 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75124 * width, y: -0.75124 * height), control: CGPoint(x: 0.85417 * width, y: -0.64831 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.85417 * height), control: CGPoint(x: 0.64831 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24876 * width, y: -0.75124 * height), control: CGPoint(x: 0.35169 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.14583 * width, y: -0.5 * height), control: CGPoint(x: 0.14583 * width, y: -0.64831 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24876 * width, y: -0.24876 * height), control: CGPoint(x: 0.14583 * width, y: -0.35169 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.14583 * height), control: CGPoint(x: 0.35169 * width, y: -0.14583 * height)) - path.closeSubpath() - return path.offsetBy(dx: 0, dy: height) - } - } - - struct SatisfiedEmoji: Shape { - func path(in rect: CGRect) -> Path { - var path = Path() - let width = rect.size.width - let height = rect.size.height - path.move(to: CGPoint(x: 0.65208 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.69193 * width, y: -0.57161 * height), control: CGPoint(x: 0.67552 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.70833 * width, y: -0.61146 * height), control: CGPoint(x: 0.70833 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.69193 * width, y: -0.6513 * height), control: CGPoint(x: 0.70833 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.65208 * width, y: -0.66771 * height), control: CGPoint(x: 0.67552 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.61224 * width, y: -0.6513 * height), control: CGPoint(x: 0.62865 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.59583 * width, y: -0.61146 * height), control: CGPoint(x: 0.59583 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.61224 * width, y: -0.57161 * height), control: CGPoint(x: 0.59583 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.65208 * width, y: -0.55521 * height), control: CGPoint(x: 0.62865 * width, y: -0.55521 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.34792 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.38776 * width, y: -0.57161 * height), control: CGPoint(x: 0.37135 * width, y: -0.55521 * height)) - path.addQuadCurve(to: CGPoint(x: 0.40417 * width, y: -0.61146 * height), control: CGPoint(x: 0.40417 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.38776 * width, y: -0.6513 * height), control: CGPoint(x: 0.40417 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.34792 * width, y: -0.66771 * height), control: CGPoint(x: 0.37135 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.30807 * width, y: -0.6513 * height), control: CGPoint(x: 0.32448 * width, y: -0.66771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.29167 * width, y: -0.61146 * height), control: CGPoint(x: 0.29167 * width, y: -0.6349 * height)) - path.addQuadCurve(to: CGPoint(x: 0.30807 * width, y: -0.57161 * height), control: CGPoint(x: 0.29167 * width, y: -0.58802 * height)) - path.addQuadCurve(to: CGPoint(x: 0.34792 * width, y: -0.55521 * height), control: CGPoint(x: 0.32448 * width, y: -0.55521 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.27187 * height)) - path.addQuadCurve(to: CGPoint(x: 0.62656 * width, y: -0.30885 * height), control: CGPoint(x: 0.56875 * width, y: -0.27187 * height)) - path.addQuadCurve(to: CGPoint(x: 0.71042 * width, y: -0.40937 * height), control: CGPoint(x: 0.68437 * width, y: -0.34583 * height)) - path.addLine(to: CGPoint(x: 0.65625 * width, y: -0.40937 * height)) - path.addQuadCurve(to: CGPoint(x: 0.59062 * width, y: -0.34531 * height), control: CGPoint(x: 0.63229 * width, y: -0.36771 * height)) - path.addQuadCurve(to: CGPoint(x: 0.50052 * width, y: -0.32292 * height), control: CGPoint(x: 0.54896 * width, y: -0.32292 * height)) - path.addQuadCurve(to: CGPoint(x: 0.4099 * width, y: -0.34479 * height), control: CGPoint(x: 0.45208 * width, y: -0.32292 * height)) - path.addQuadCurve(to: CGPoint(x: 0.34479 * width, y: -0.40937 * height), control: CGPoint(x: 0.36771 * width, y: -0.36667 * height)) - path.addLine(to: CGPoint(x: 0.28958 * width, y: -0.40937 * height)) - path.addQuadCurve(to: CGPoint(x: 0.37396 * width, y: -0.30885 * height), control: CGPoint(x: 0.31667 * width, y: -0.34583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.27187 * height), control: CGPoint(x: 0.43125 * width, y: -0.27187 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.11615 * height), control: CGPoint(x: 0.41354 * width, y: -0.08333 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.20521 * height), control: CGPoint(x: 0.26146 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.3375 * height), control: CGPoint(x: 0.14896 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.08333 * width, y: -0.5 * height), control: CGPoint(x: 0.08333 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.11615 * width, y: -0.6625 * height), control: CGPoint(x: 0.08333 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.20521 * width, y: -0.79479 * height), control: CGPoint(x: 0.14896 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.3375 * width, y: -0.88385 * height), control: CGPoint(x: 0.26146 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.91667 * height), control: CGPoint(x: 0.41354 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.88385 * height), control: CGPoint(x: 0.58646 * width, y: -0.91667 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.79479 * height), control: CGPoint(x: 0.73854 * width, y: -0.85104 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.6625 * height), control: CGPoint(x: 0.85104 * width, y: -0.73854 * height)) - path.addQuadCurve(to: CGPoint(x: 0.91667 * width, y: -0.5 * height), control: CGPoint(x: 0.91667 * width, y: -0.58646 * height)) - path.addQuadCurve(to: CGPoint(x: 0.88385 * width, y: -0.3375 * height), control: CGPoint(x: 0.91667 * width, y: -0.41354 * height)) - path.addQuadCurve(to: CGPoint(x: 0.79479 * width, y: -0.20521 * height), control: CGPoint(x: 0.85104 * width, y: -0.26146 * height)) - path.addQuadCurve(to: CGPoint(x: 0.6625 * width, y: -0.11615 * height), control: CGPoint(x: 0.73854 * width, y: -0.14896 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.08333 * height), control: CGPoint(x: 0.58646 * width, y: -0.08333 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.5 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.5 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75124 * width, y: -0.24876 * height), control: CGPoint(x: 0.64831 * width, y: -0.14583 * height)) - path.addQuadCurve(to: CGPoint(x: 0.85417 * width, y: -0.5 * height), control: CGPoint(x: 0.85417 * width, y: -0.35169 * height)) - path.addQuadCurve(to: CGPoint(x: 0.75124 * width, y: -0.75124 * height), control: CGPoint(x: 0.85417 * width, y: -0.64831 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.85417 * height), control: CGPoint(x: 0.64831 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24876 * width, y: -0.75124 * height), control: CGPoint(x: 0.35169 * width, y: -0.85417 * height)) - path.addQuadCurve(to: CGPoint(x: 0.14583 * width, y: -0.5 * height), control: CGPoint(x: 0.14583 * width, y: -0.64831 * height)) - path.addQuadCurve(to: CGPoint(x: 0.24876 * width, y: -0.24876 * height), control: CGPoint(x: 0.14583 * width, y: -0.35169 * height)) - path.addQuadCurve(to: CGPoint(x: 0.5 * width, y: -0.14583 * height), control: CGPoint(x: 0.35169 * width, y: -0.14583 * height)) - path.closeSubpath() - return path.offsetBy(dx: 0, dy: height) - } - } - - struct CheckIcon: Shape { - func path(in rect: CGRect) -> Path { - var path = Path() - let width = rect.size.width - let height = rect.size.height - path.move(to: CGPoint(x: 0.33173 * width, y: 0.89102 * height)) - path.addLine(to: CGPoint(x: 0.29858 * width, y: 0.93522 * height)) - path.addCurve(to: CGPoint(x: 0.33173 * width, y: 0.95352 * height), control1: CGPoint(x: 0.30738 * width, y: 0.94694 * height), control2: CGPoint(x: 0.3193 * width, y: 0.95352 * height)) - path.addCurve(to: CGPoint(x: 0.36488 * width, y: 0.93522 * height), control1: CGPoint(x: 0.34416 * width, y: 0.95352 * height), control2: CGPoint(x: 0.35609 * width, y: 0.94694 * height)) - path.addLine(to: CGPoint(x: 0.33173 * width, y: 0.89102 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.97064 * width, y: 0.12753 * height)) - path.addCurve(to: CGPoint(x: 0.97064 * width, y: 0.03914 * height), control1: CGPoint(x: 0.98895 * width, y: 0.10312 * height), control2: CGPoint(x: 0.98895 * width, y: 0.06355 * height)) - path.addCurve(to: CGPoint(x: 0.90436 * width, y: 0.03914 * height), control1: CGPoint(x: 0.95234 * width, y: 0.01473 * height), control2: CGPoint(x: 0.92266 * width, y: 0.01473 * height)) - path.addLine(to: CGPoint(x: 0.97064 * width, y: 0.12753 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.09565 * width, y: 0.48786 * height)) - path.addCurve(to: CGPoint(x: 0.02935 * width, y: 0.48786 * height), control1: CGPoint(x: 0.07734 * width, y: 0.46345 * height), control2: CGPoint(x: 0.04766 * width, y: 0.46345 * height)) - path.addCurve(to: CGPoint(x: 0.02935 * width, y: 0.57625 * height), control1: CGPoint(x: 0.01105 * width, y: 0.51226 * height), control2: CGPoint(x: 0.01105 * width, y: 0.55184 * height)) - path.addLine(to: CGPoint(x: 0.09565 * width, y: 0.48786 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.36488 * width, y: 0.93522 * height)) - path.addLine(to: CGPoint(x: 0.97064 * width, y: 0.12753 * height)) - path.addLine(to: CGPoint(x: 0.90436 * width, y: 0.03914 * height)) - path.addLine(to: CGPoint(x: 0.29858 * width, y: 0.84683 * height)) - path.addLine(to: CGPoint(x: 0.36488 * width, y: 0.93522 * height)) - path.closeSubpath() - path.move(to: CGPoint(x: 0.02935 * width, y: 0.57625 * height)) - path.addLine(to: CGPoint(x: 0.29858 * width, y: 0.93522 * height)) - path.addLine(to: CGPoint(x: 0.36488 * width, y: 0.84683 * height)) - path.addLine(to: CGPoint(x: 0.09565 * width, y: 0.48786 * height)) - path.addLine(to: CGPoint(x: 0.02935 * width, y: 0.57625 * height)) - path.closeSubpath() - return path - } - } - - #Preview { - VStack { - HStack { - VeryDissatisfiedEmoji() - .frame(width: 48, height: 48) - .foregroundColor(.blue) - DissatisfiedEmoji().frame(width: 48, height: 48) - NeutralEmoji().frame(width: 48, height: 48) - SatisfiedEmoji().frame(width: 48, height: 48) - VerySatisfiedEmoji().frame(width: 48, height: 48) - } - HStack { - CheckIcon().frame(width: 16, height: 12) - } - } - } -#endif -// swiftlint:enable line_length diff --git a/Pods/PostHog/PostHog/Surveys/Utils/SegmentedControl.swift b/Pods/PostHog/PostHog/Surveys/Utils/SegmentedControl.swift deleted file mode 100644 index 2e152aa..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/SegmentedControl.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// SegmentedControl.swift -// PostHog -// -// Created by Ioannis Josephides on 11/03/2025. -// - -#if os(iOS) - import SwiftUI - - struct SegmentedControl: View { - var range: ClosedRange - var height: CGFloat = 45 - - @Binding var selectedValue: Int? - @ViewBuilder var segmentView: (_ value: Int, _ selected: Bool) -> Segment - @ViewBuilder var separatorView: (_ value: Int, _ selected: Bool) -> Separator - @ViewBuilder var indicatorView: (CGSize) -> Indicator - - @State private var minX: CGFloat = .zero - - var body: some View { - GeometryReader { - let size = $0.size - let containerWidthForEachTab = size.width / CGFloat(range.count) - - HStack(spacing: 0) { - ForEach(range, id: \.self) { value in - let isSelected = selectedValue == value - Button { - if selectedValue == value { - withAnimation(.snappy(duration: 0.25, extraBounce: 0)) { - selectedValue = nil - } - - } else { - let index = value - range.lowerBound - if selectedValue == nil { - minX = containerWidthForEachTab * CGFloat(index) - withAnimation(.snappy(duration: 0.25, extraBounce: 0)) { - selectedValue = value - } - } else { - selectedValue = selectedValue == value ? nil : value - withAnimation(.snappy(duration: 0.25, extraBounce: 0)) { - minX = containerWidthForEachTab * CGFloat(index) - } - } - } - - } label: { - segmentView(value, isSelected) - .contentShape(.rect) - .frame(maxWidth: .infinity, maxHeight: .infinity) - } - .buttonStyle(.borderless) - .animation(.snappy, value: selectedValue) - .background( - Group { - if value == range.lowerBound, selectedValue != nil { - GeometryReader { - let size = $0.size - indicatorView(size) - .frame(width: size.width, height: size.height, alignment: .leading) - .offset(x: minX) - } - } - }, - alignment: .leading - ) - .overlay( - separatorView(value, isSelected) - ) - } - } - .preference(key: SizeKey.self, value: size) - .onPreferenceChange(SizeKey.self) { _ in - if let selectedValue { - let index = selectedValue - range.lowerBound - minX = containerWidthForEachTab * CGFloat(index) - } - } - } - .frame(height: height) - } - } - - private struct SizeKey: PreferenceKey { - static var defaultValue: CGSize = .zero - static func reduce(value: inout CGSize, nextValue: () -> CGSize) { - value = nextValue() - } - } - -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/Survey+Util.swift b/Pods/PostHog/PostHog/Surveys/Utils/Survey+Util.swift deleted file mode 100644 index f2f4fdc..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/Survey+Util.swift +++ /dev/null @@ -1,306 +0,0 @@ -// https://gist.github.com/nbasham/3b2de0566d5f716894fc -// -// Survey+Util.swift -// previously Color+HexAndCSSColorNames.swift -// -// Created by Norman Basham on 12/8/15. -// Copyright ©2018 Black Labs. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#if os(iOS) - import SwiftUI - import UIKit - - // swiftlint:disable identifier_name - public extension UIColor { - /** - Creates an immuatble UIColor instance specified by a hex string, CSS color name, or nil. - - - parameter hexString: A case insensitive String? representing a hex or CSS value e.g. - - - **"abc"** - - **"abc7"** - - **"#abc7"** - - **"00FFFF"** - - **"#00FFFF"** - - **"00FFFF77"** - - **"Orange", "Azure", "Tomato"** Modern browsers support 140 color names () - - **"Clear"** [UIColor clearColor] - - **"Transparent"** [UIColor clearColor] - - **nil** [UIColor clearColor] - - **empty string** [UIColor clearColor] - */ - convenience init(hex: String?) { - let normalizedHexString: String = UIColor.normalize(hex) - var c: CUnsignedLongLong = 0 - Scanner(string: normalizedHexString).scanHexInt64(&c) - self.init( - red: UIColorMasks.redValue(CUnsignedInt(c)), - green: UIColorMasks.greenValue(CUnsignedInt(c)), - blue: UIColorMasks.blueValue(CUnsignedInt(c)), - alpha: UIColorMasks.alphaValue(CUnsignedInt(c)) - ) - } - - /** - Returns a hex equivalent of this UIColor. - - - Parameter includeAlpha: Optional parameter to include the alpha hex. - - color.hexDescription() -> "ff0000" - - color.hexDescription(true) -> "ff0000aa" - - - Returns: A new string with `String` with the color's hexidecimal value. - */ - func hexDescription(_ includeAlpha: Bool = false) -> String { - guard cgColor.numberOfComponents == 4 else { - return "Color not RGB." - } - let a = cgColor.components!.map { Int($0 * CGFloat(255)) } - let color = String(format: "%02x%02x%02x", a[0], a[1], a[2]) - if includeAlpha { - let alpha = String(format: "%02x", a[3]) - return "\(color)\(alpha)" - } - return color - } - - fileprivate enum UIColorMasks: CUnsignedInt { - case redMask = 0xFF000000 - case greenMask = 0x00FF0000 - case blueMask = 0x0000FF00 - case alphaMask = 0x000000FF - - static func redValue(_ value: CUnsignedInt) -> CGFloat { - CGFloat((value & redMask.rawValue) >> 24) / 255.0 - } - - static func greenValue(_ value: CUnsignedInt) -> CGFloat { - CGFloat((value & greenMask.rawValue) >> 16) / 255.0 - } - - static func blueValue(_ value: CUnsignedInt) -> CGFloat { - CGFloat((value & blueMask.rawValue) >> 8) / 255.0 - } - - static func alphaValue(_ value: CUnsignedInt) -> CGFloat { - CGFloat(value & alphaMask.rawValue) / 255.0 - } - } - - fileprivate static func normalize(_ hex: String?) -> String { - guard var hexString = hex else { - return "00000000" - } - if let cssColor = cssToHexDictionary[hexString.uppercased()] { - return cssColor.count == 8 ? cssColor : cssColor + "ff" - } - if hexString.hasPrefix("#") { - hexString = String(hexString.dropFirst()) - } - if hexString.count == 3 || hexString.count == 4 { - hexString = hexString.map { "\($0)\($0)" }.joined() - } - let hasAlpha = hexString.count > 7 - if !hasAlpha { - hexString += "ff" - } - return hexString - } - - /** - All modern browsers support the following 140 color names (see http://www.w3schools.com/cssref/css_colornames.asp) - */ - fileprivate static func hexFromCssName(_ cssName: String) -> String { - let key = cssName.uppercased() - if let hex = cssToHexDictionary[key] { - return hex - } - return cssName - } - - fileprivate static let cssToHexDictionary: [String: String] = [ - "CLEAR": "00000000", - "TRANSPARENT": "00000000", - "": "00000000", - "ALICEBLUE": "F0F8FF", - "ANTIQUEWHITE": "FAEBD7", - "AQUA": "00FFFF", - "AQUAMARINE": "7FFFD4", - "AZURE": "F0FFFF", - "BEIGE": "F5F5DC", - "BISQUE": "FFE4C4", - "BLACK": "000000", - "BLANCHEDALMOND": "FFEBCD", - "BLUE": "0000FF", - "BLUEVIOLET": "8A2BE2", - "BROWN": "A52A2A", - "BURLYWOOD": "DEB887", - "CADETBLUE": "5F9EA0", - "CHARTREUSE": "7FFF00", - "CHOCOLATE": "D2691E", - "CORAL": "FF7F50", - "CORNFLOWERBLUE": "6495ED", - "CORNSILK": "FFF8DC", - "CRIMSON": "DC143C", - "CYAN": "00FFFF", - "DARKBLUE": "00008B", - "DARKCYAN": "008B8B", - "DARKGOLDENROD": "B8860B", - "DARKGRAY": "A9A9A9", - "DARKGREY": "A9A9A9", - "DARKGREEN": "006400", - "DARKKHAKI": "BDB76B", - "DARKMAGENTA": "8B008B", - "DARKOLIVEGREEN": "556B2F", - "DARKORANGE": "FF8C00", - "DARKORCHID": "9932CC", - "DARKRED": "8B0000", - "DARKSALMON": "E9967A", - "DARKSEAGREEN": "8FBC8F", - "DARKSLATEBLUE": "483D8B", - "DARKSLATEGRAY": "2F4F4F", - "DARKSLATEGREY": "2F4F4F", - "DARKTURQUOISE": "00CED1", - "DARKVIOLET": "9400D3", - "DEEPPINK": "FF1493", - "DEEPSKYBLUE": "00BFFF", - "DIMGRAY": "696969", - "DIMGREY": "696969", - "DODGERBLUE": "1E90FF", - "FIREBRICK": "B22222", - "FLORALWHITE": "FFFAF0", - "FORESTGREEN": "228B22", - "FUCHSIA": "FF00FF", - "GAINSBORO": "DCDCDC", - "GHOSTWHITE": "F8F8FF", - "GOLD": "FFD700", - "GOLDENROD": "DAA520", - "GRAY": "808080", - "GREY": "808080", - "GREEN": "008000", - "GREENYELLOW": "ADFF2F", - "HONEYDEW": "F0FFF0", - "HOTPINK": "FF69B4", - "INDIANRED": "CD5C5C", - "INDIGO": "4B0082", - "IVORY": "FFFFF0", - "KHAKI": "F0E68C", - "LAVENDER": "E6E6FA", - "LAVENDERBLUSH": "FFF0F5", - "LAWNGREEN": "7CFC00", - "LEMONCHIFFON": "FFFACD", - "LIGHTBLUE": "ADD8E6", - "LIGHTCORAL": "F08080", - "LIGHTCYAN": "E0FFFF", - "LIGHTGOLDENRODYELLOW": "FAFAD2", - "LIGHTGRAY": "D3D3D3", - "LIGHTGREY": "D3D3D3", - "LIGHTGREEN": "90EE90", - "LIGHTPINK": "FFB6C1", - "LIGHTSALMON": "FFA07A", - "LIGHTSEAGREEN": "20B2AA", - "LIGHTSKYBLUE": "87CEFA", - "LIGHTSLATEGRAY": "778899", - "LIGHTSLATEGREY": "778899", - "LIGHTSTEELBLUE": "B0C4DE", - "LIGHTYELLOW": "FFFFE0", - "LIME": "00FF00", - "LIMEGREEN": "32CD32", - "LINEN": "FAF0E6", - "MAGENTA": "FF00FF", - "MAROON": "800000", - "MEDIUMAQUAMARINE": "66CDAA", - "MEDIUMBLUE": "0000CD", - "MEDIUMORCHID": "BA55D3", - "MEDIUMPURPLE": "9370DB", - "MEDIUMSEAGREEN": "3CB371", - "MEDIUMSLATEBLUE": "7B68EE", - "MEDIUMSPRINGGREEN": "00FA9A", - "MEDIUMTURQUOISE": "48D1CC", - "MEDIUMVIOLETRED": "C71585", - "MIDNIGHTBLUE": "191970", - "MINTCREAM": "F5FFFA", - "MISTYROSE": "FFE4E1", - "MOCCASIN": "FFE4B5", - "NAVAJOWHITE": "FFDEAD", - "NAVY": "000080", - "OLDLACE": "FDF5E6", - "OLIVE": "808000", - "OLIVEDRAB": "6B8E23", - "ORANGE": "FFA500", - "ORANGERED": "FF4500", - "ORCHID": "DA70D6", - "PALEGOLDENROD": "EEE8AA", - "PALEGREEN": "98FB98", - "PALETURQUOISE": "AFEEEE", - "PALEVIOLETRED": "DB7093", - "PAPAYAWHIP": "FFEFD5", - "PEACHPUFF": "FFDAB9", - "PERU": "CD853F", - "PINK": "FFC0CB", - "PLUM": "DDA0DD", - "POWDERBLUE": "B0E0E6", - "PURPLE": "800080", - "RED": "FF0000", - "ROSYBROWN": "BC8F8F", - "ROYALBLUE": "4169E1", - "SADDLEBROWN": "8B4513", - "SALMON": "FA8072", - "SANDYBROWN": "F4A460", - "SEAGREEN": "2E8B57", - "SEASHELL": "FFF5EE", - "SIENNA": "A0522D", - "SILVER": "C0C0C0", - "SKYBLUE": "87CEEB", - "SLATEBLUE": "6A5ACD", - "SLATEGRAY": "708090", - "SLATEGREY": "708090", - "SNOW": "FFFAFA", - "SPRINGGREEN": "00FF7F", - "STEELBLUE": "4682B4", - "TAN": "D2B48C", - "TEAL": "008080", - "THISTLE": "D8BFD8", - "TOMATO": "FF6347", - "TURQUOISE": "40E0D0", - "VIOLET": "EE82EE", - "WHEAT": "F5DEB3", - "WHITE": "FFFFFF", - "WHITESMOKE": "F5F5F5", - "YELLOW": "FFFF00", - "YELLOWGREEN": "9ACD32", - ] - } - - extension Color { - @available(iOS 15.0, *) - func getContrastingTextColor() -> Color { - var r, g, b, a: CGFloat - (r, g, b, a) = (0, 0, 0, 0) - UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a) - let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b - return luminance < 0.6 ? .white : .black - } - } - - // swiftlint:enable identifier_name -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/SurveyButton.swift b/Pods/PostHog/PostHog/Surveys/Utils/SurveyButton.swift deleted file mode 100644 index 7ac992f..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/SurveyButton.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// SurveyButton.swift -// PostHog -// -// Created by Ioannis Josephides on 11/03/2025. -// - -#if os(iOS) - - import SwiftUI - - @available(iOS 15.0, *) - struct SurveyButtonStyle: ButtonStyle { - @Environment(\.surveyAppearance) private var appearance - @Environment(\.isEnabled) private var isEnabled - - func makeBody(configuration: Configuration) -> some View { - configuration.label - .font(.body.bold()) - .frame(maxWidth: .infinity) - .shadow(color: Color.black.opacity(0.12), radius: 0, x: 0, y: -1) // Text shadow - .padding(12) - .foregroundStyle(appearance.submitButtonTextColor) - .background( - RoundedRectangle(cornerRadius: 6) - .fill(appearance.submitButtonColor) - .shadow(color: Color.black.opacity(0.15), radius: 2, x: 0, y: 2) // Box shadow - ) - .contentShape(Rectangle()) - .opacity(configuration.isPressed ? 0.80 : opacity) - } - - private var opacity: Double { - isEnabled ? 1.0 : 0.5 - } - } -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/SurveyPresentationDetentsRepresentable.swift b/Pods/PostHog/PostHog/Surveys/Utils/SurveyPresentationDetentsRepresentable.swift deleted file mode 100644 index d9b311a..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/SurveyPresentationDetentsRepresentable.swift +++ /dev/null @@ -1,122 +0,0 @@ -// -// SurveyPresentationDetentsRepresentable.swift -// PostHog -// -// Created by Ioannis Josephides on 22/03/2025. -// - -#if os(iOS) - - import SwiftUI - - @available(iOS 15.0, *) - struct SurveyPresentationDetentsRepresentable: UIViewControllerRepresentable { - enum Detent: Hashable, Identifiable, Comparable { - case medium - case large - case height(_ value: CGFloat) - - var toPresentationDetents: UISheetPresentationController.Detent { - switch self { - case .medium: .medium() - case .large: - if #available(iOS 16.0, *) { - // almost large detent, so that background view is not scaled - .custom(identifier: id, resolver: { context in context.maximumDetentValue - 0.5 }) - } else { - .large() - } - case let .height(value): - if #available(iOS 16.0, *) { - if value > 0 { - .custom(identifier: id, resolver: { _ in value }) - } else { - .medium() - } - } else { - .medium() - } - } - } - - var id: UISheetPresentationController.Detent.Identifier { - switch self { - case .medium: .init("com.apple.UIKit.medium") - case .large: - if #available(iOS 16.0, *) { - .init("posthog.detent.almostLarge") - } else { - .init("com.apple.UIKit.large") - } - case let .height(value): - if #available(iOS 16.0, *) { - if value > 0 { - .init("posthog.detent.customHeight.\(value)") - } else { - .init("com.apple.UIKit.medium") - } - } else { - .init("com.apple.UIKit.medium") - } - } - } - } - - let detents: [Detent] - - func makeUIViewController(context _: Context) -> Controller { - Controller(detents: detents) - } - - func updateUIViewController(_ controller: Controller, context _: Context) { - controller.detents = detents - DispatchQueue.main.async(execute: controller.update) - } - - final class Controller: UIViewController, UISheetPresentationControllerDelegate { - var detents: [Detent] - - init(detents: [Detent]) { - self.detents = detents - super.init(nibName: nil, bundle: nil) - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - detents = [] - super.init(nibName: nil, bundle: nil) - } - - func update() { - let newDetents = detents.map(\.toPresentationDetents) - - if let controller = sheetPresentationController { - controller.detents = newDetents - - // present as bottom sheet on compact-size (e.g landscape) - if #available(iOS 16.0, *) { - controller.prefersEdgeAttachedInCompactHeight = true - controller.widthFollowsPreferredContentSizeWhenEdgeAttached = true - } else { - // Getting some weird crash on iOS 15.5 when setting this to true. Disable for now - // This means that on iOS 15.0 landscape mode presentation will be full screen - controller.prefersEdgeAttachedInCompactHeight = false - controller.widthFollowsPreferredContentSizeWhenEdgeAttached = false - } - // scrolling with expand the bottom sheet if needed - controller.prefersScrollingExpandsWhenScrolledToEdge = true - // show drag indicator if bottom sheet is expandable - controller.prefersGrabberVisible = detents.count > 1 - // always dim background - controller.presentingViewController.view?.tintAdjustmentMode = .dimmed - } - } - - override func viewWillTransition(to size: CGSize, with coordinator: any UIViewControllerTransitionCoordinator) { - super.viewWillTransition(to: size, with: coordinator) - DispatchQueue.main.async(execute: update) - } - } - } - -#endif diff --git a/Pods/PostHog/PostHog/Surveys/Utils/SwiftUI+Util.swift b/Pods/PostHog/PostHog/Surveys/Utils/SwiftUI+Util.swift deleted file mode 100644 index 5773836..0000000 --- a/Pods/PostHog/PostHog/Surveys/Utils/SwiftUI+Util.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// SwiftUI+Util.swift -// PostHog -// -// Created by Ioannis Josephides on 10/03/2025. -// - -#if os(iOS) - import SwiftUI - - extension View { - /// Reads frame changes of current view in a coordinate space (default global) - @available(iOS 14.0, *) - func readFrame( - in coordinateSpace: CoordinateSpace = .global, - onFrame: @escaping (CGRect) -> Void - ) -> some View { - modifier( - ReadFrameModifier( - coordinateSpace: coordinateSpace, - onFrame: onFrame - ) - ) - } - - /// Reads current view's safe area insets - @available(iOS 14.0, *) - func readSafeAreaInsets( - onSafeAreaInsets: @escaping (EdgeInsets) -> Void - ) -> some View { - modifier( - ReadSafeAreaInsetsModifier( - onSafeAreaInsets: onSafeAreaInsets - ) - ) - } - - /// Type-erases a View - var erasedToAnyView: AnyView { - AnyView(self) - } - } - - @available(iOS 14.0, *) - private struct ReadFrameModifier: ViewModifier { - /// Helper for notifying parents for child view frame changes - struct FramePreferenceKey: PreferenceKey { - static var defaultValue: CGRect = .zero - static func reduce(value _: inout CGRect, nextValue _: () -> CGRect) { - // nothing - } - } - - let coordinateSpace: CoordinateSpace - let onFrame: (CGRect) -> Void - - func body(content: Content) -> some View { - content - .background( - GeometryReader { proxy in - Color.clear - .preference( - key: FramePreferenceKey.self, - value: proxy.frame(in: coordinateSpace) - ) - } - ) - .onPreferenceChange(FramePreferenceKey.self, perform: onFrame) - } - } - - @available(iOS 14.0, *) - private struct ReadSafeAreaInsetsModifier: ViewModifier { - /// Helper for notifying parents for child view's safe area insets - struct SafeAreaInsetsPreferenceKey: PreferenceKey { - static var defaultValue: EdgeInsets = .init() - static func reduce(value _: inout EdgeInsets, nextValue _: () -> EdgeInsets) { - // nothing - } - } - - let onSafeAreaInsets: (EdgeInsets) -> Void - - @State private var safeAreaInsets: EdgeInsets = .init() - - func body(content: Content) -> some View { - ZStack { - content - .background( - GeometryReader { proxy in - Color.clear - .onChange(of: proxy.safeAreaInsets) { size in - safeAreaInsets = size - } - .preference( - key: SafeAreaInsetsPreferenceKey.self, - value: safeAreaInsets - ) - } - ) - } - .onPreferenceChange(SafeAreaInsetsPreferenceKey.self, perform: onSafeAreaInsets) - } - } -#endif diff --git a/Pods/PostHog/PostHog/SwiftUI/PostHogMaskViewModifier.swift b/Pods/PostHog/PostHog/SwiftUI/PostHogMaskViewModifier.swift deleted file mode 100644 index 227674e..0000000 --- a/Pods/PostHog/PostHog/SwiftUI/PostHogMaskViewModifier.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// PostHogMaskViewModifier.swift -// PostHog -// -// Created by Yiannis Josephides on 09/10/2024. -// - -#if os(iOS) && canImport(SwiftUI) - - import SwiftUI - - public extension View { - /** - Marks a SwiftUI View to be masked in PostHog session replay recordings. - - Because of the nature of how we intercept SwiftUI view hierarchy (and how it maps to UIKit), - we can't always be 100% confident that a view should be masked and may accidentally mark a - sensitive view as non-sensitive instead. - - Use this modifier to explicitly mask sensitive views in session replay recordings. - - For example: - ```swift - // This view will be masked in recordings - SensitiveDataView() - .postHogMask() - - // Conditionally mask based on a flag - SensitiveDataView() - .postHogMask(shouldMask) - ``` - - - Parameter isEnabled: Whether masking should be enabled. Defaults to true. - - Returns: A modified view that will be masked in session replay recordings when enabled - */ - func postHogMask(_ isEnabled: Bool = true) -> some View { - modifier( - PostHogTagViewModifier { uiViews in - uiViews.forEach { $0.postHogNoCapture = isEnabled } - } onRemove: { uiViews in - uiViews.forEach { $0.postHogNoCapture = false } - } - ) - } - } - - extension UIView { - var postHogNoCapture: Bool { - get { objc_getAssociatedObject(self, &AssociatedKeys.phNoCapture) as? Bool ?? false } - set { objc_setAssociatedObject(self, &AssociatedKeys.phNoCapture, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - } -#endif diff --git a/Pods/PostHog/PostHog/SwiftUI/PostHogNoMaskViewModifier.swift b/Pods/PostHog/PostHog/SwiftUI/PostHogNoMaskViewModifier.swift deleted file mode 100644 index e4c1c45..0000000 --- a/Pods/PostHog/PostHog/SwiftUI/PostHogNoMaskViewModifier.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// PostHogNoMaskViewModifier.swift -// PostHog -// -// Created by Yiannis Josephides on 09/10/2024. -// - -#if os(iOS) && canImport(SwiftUI) - - import SwiftUI - - public extension View { - /** - Marks a SwiftUI View to be excluded from masking in PostHog session replay recordings. - - There are cases where PostHog SDK will unintentionally mask some SwiftUI views. - - Because of the nature of how we intercept SwiftUI view hierarchy (and how it maps to UIKit), - we can't always be 100% confident that a view should be masked. For that reason, we prefer to - take a proactive and prefer to mask views if we're not sure. - - Use this modifier to prevent views from being masked in session replay recordings. - - For example: - ```swift - // This view may be accidentally masked by PostHog SDK - SomeSafeView() - - // This custom view (and all its subviews) will not be masked in recordings - SomeSafeView() - .postHogNoMask() - ``` - - - Returns: A modified view that will not be masked in session replay recordings - */ - func postHogNoMask() -> some View { - modifier( - PostHogTagViewModifier { uiViews in - uiViews.forEach { $0.postHogNoMask = true } - } onRemove: { uiViews in - uiViews.forEach { $0.postHogNoMask = false } - } - ) - } - } - - extension UIView { - var postHogNoMask: Bool { - get { objc_getAssociatedObject(self, &AssociatedKeys.phNoMask) as? Bool ?? false } - set { objc_setAssociatedObject(self, &AssociatedKeys.phNoMask, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - } - -#endif diff --git a/Pods/PostHog/PostHog/SwiftUI/PostHogSwiftUIViewModifiers.swift b/Pods/PostHog/PostHog/SwiftUI/PostHogSwiftUIViewModifiers.swift deleted file mode 100644 index b809426..0000000 --- a/Pods/PostHog/PostHog/SwiftUI/PostHogSwiftUIViewModifiers.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// PostHogSwiftUIViewModifiers.swift -// PostHog -// -// Created by Manoel Aranda Neto on 05.09.24. -// - -#if canImport(SwiftUI) - import Foundation - import SwiftUI - - public extension View { - /** - Marks a SwiftUI View to be tracked as a $screen event in PostHog when onAppear is called. - - - Parameters: - - screenName: The name of the screen. Defaults to the type of the view. - - properties: Additional properties to be tracked with the screen. - - postHog: The instance to be used when sending the $screen event - - Returns: A modified view that will be tracked as a screen in PostHog. - */ - func postHogScreenView(_ screenName: String? = nil, - _ properties: [String: Any]? = nil, - postHog: PostHogSDK? = nil) -> some View - { - let viewEventName = screenName ?? "\(type(of: self))" - return modifier(PostHogSwiftUIViewModifier(viewEventName: viewEventName, - screenEvent: true, - properties: properties, - postHog: postHog)) - } - - func postHogViewSeen(_ event: String, - _ properties: [String: Any]? = nil, - postHog: PostHogSDK? = nil) -> some View - { - modifier(PostHogSwiftUIViewModifier(viewEventName: event, - screenEvent: false, - properties: properties, - postHog: postHog)) - } - } - - private struct PostHogSwiftUIViewModifier: ViewModifier { - let viewEventName: String - - let screenEvent: Bool - - let properties: [String: Any]? - - let postHog: PostHogSDK? - - func body(content: Content) -> some View { - content.onAppear { - if screenEvent { - instance.screen(viewEventName, properties: properties) - } else { - instance.capture(viewEventName, properties: properties) - } - } - } - - private var instance: PostHogSDK { - postHog ?? PostHogSDK.shared - } - } - -#endif diff --git a/Pods/PostHog/PostHog/SwiftUI/PostHogTagViewModifier.swift b/Pods/PostHog/PostHog/SwiftUI/PostHogTagViewModifier.swift deleted file mode 100644 index 68cfefb..0000000 --- a/Pods/PostHog/PostHog/SwiftUI/PostHogTagViewModifier.swift +++ /dev/null @@ -1,371 +0,0 @@ -// -// PostHogTagViewModifier.swift -// PostHog -// -// Created by Yiannis Josephides on 19/12/2024. -// - -// Inspired from: https://github.com/siteline/swiftui-introspect - -#if os(iOS) && canImport(SwiftUI) - import SwiftUI - - typealias PostHogTagViewHandler = ([UIView]) -> Void - - /** - This is a helper view modifier for retrieving a list of underlying UIKit views for the current SwiftUI view. - - This implementation injects two hidden views into the SwiftUI view hierarchy, with the purpose of using them to retrieve the generated UIKit views for this SwiftUI view. - - The two injected views basically sandwich the current SwiftUI view: - - The first view is an anchor view, which defines how far **down** we need to traverse the view hierarchy (added as a background view). - - The second view is a tagger view, which defines how far **up** we traverse the view hierarchy (added as an overlay view). - - Any view in between the two should be the generated UIKit views that correspond to the current View - - ``` - View Hierarchy Tree: - - UIHostingController - │ - ▼ - _UIHostingView (Common ancestor) - │ - ┌──────┴──────┐ - ▼ ▼ - UnrelatedView | - │ - PostHogTagView - (overlay) - │ - ▼ - _UIGeneratedView (e.g generated views in an HStack) - │ - ▼ - _UIGeneratedView (e.g generated views in an HStack) - │ - ▼ - PostHogTagAnchorView - (background) - - The general approach is: - - 1. PostHogTagAnchorView injected as background (bottom boundary) - 2. PostHogTagView injected as overlay (top boundary) - 3. System renders SwiftUI view hierarchy in UIKit - 4. Find the common ancestor of the PostHogTagAnchorView and PostHogTagView (e.g _UIHostingView) - 5. Retrieve all of the descendants of common ancestor that are between PostHogTagView and PostHogTagAnchorView (excluding tagged views) - - This logic is implemented in the `getTargetViews` function, which is called from PostHogTagView. - - ``` - */ - struct PostHogTagViewModifier: ViewModifier { - private let id = UUID() - - let onChange: PostHogTagViewHandler - let onRemove: PostHogTagViewHandler - - /** - This is a helper view modifier for retrieving a list of underlying UIKit views for the current SwiftUI view. - - If, for example, this modifier is applied on an instance of an HStack, the returned list will contain the underlying UIKit views embedded in the HStack. - For single views, the returned list will contain a single element, the view itself. - - - Parameters: - - onChange: called when the underlying UIKit views are detected, or when they are layed out. - - onRemove: called when the underlying UIKit views are removed from the view hierarchy, for cleanup. - */ - init(onChange: @escaping PostHogTagViewHandler, onRemove: @escaping PostHogTagViewHandler) { - self.onChange = onChange - self.onRemove = onRemove - } - - func body(content: Content) -> some View { - content - .background( - PostHogTagAnchorView(id: id) - .accessibility(hidden: true) - .frame(width: 0, height: 0) - ) - .overlay( - PostHogTagView(id: id, onChange: onChange, onRemove: onRemove) - .accessibility(hidden: true) - .frame(width: 0, height: 0) - ) - } - } - - struct PostHogTagView: UIViewRepresentable { - final class Coordinator { - var onChangeHandler: PostHogTagViewHandler? - var onRemoveHandler: PostHogTagViewHandler? - - private var _targets: [Weak] - var cachedTargets: [UIView] { - get { _targets.compactMap(\.value) } - set { _targets = newValue.map(Weak.init) } - } - - init( - onRemove: PostHogTagViewHandler? - ) { - _targets = [] - onRemoveHandler = onRemove - } - } - - @Binding - private var observed: Void // workaround for state changes not triggering view updates - private let id: UUID - private let onChangeHandler: PostHogTagViewHandler? - private let onRemoveHandler: PostHogTagViewHandler? - - init( - id: UUID, - onChange: PostHogTagViewHandler?, - onRemove: PostHogTagViewHandler? - ) { - _observed = .constant(()) - self.id = id - onChangeHandler = onChange - onRemoveHandler = onRemove - } - - func makeCoordinator() -> Coordinator { - // dismantleUIView is Static, so we need to store the onRemoveHandler - // somewhere where we can access it during view distruction - Coordinator(onRemove: onRemoveHandler) - } - - func makeUIView(context: Context) -> PostHogTagUIView { - let view = PostHogTagUIView(id: id) { controller in - let targets = getTargetViews(from: controller) - if !targets.isEmpty { - context.coordinator.cachedTargets = targets - onChangeHandler?(targets) - } - } - - return view - } - - func updateUIView(_: PostHogTagUIView, context _: Context) { - // - } - - static func dismantleUIView(_ uiView: PostHogTagUIView, coordinator: Coordinator) { - // using cached targets should be good here - let targets = coordinator.cachedTargets.isEmpty - ? getTargetViews(from: uiView) - : coordinator.cachedTargets - - if !targets.isEmpty { - coordinator.onRemoveHandler?(targets) - } - - uiView.postHogTagView = nil - uiView.handler = nil - } - } - - private let swiftUIIgnoreTypes: [AnyClass] = [ - // .clipShape or .clipped SwiftUI modifiers will add this to view hierarchy - // Not sure of its functionality, but it seems to be just a wrapper view with no visual impact - // - // We can safely ignore from list of descendant views, since it's sometimes being tagged - // for replay masking unintentionally - "SwiftUI._UIInheritedView", - ].compactMap(NSClassFromString) - - func getTargetViews(from taggerView: UIView) -> [UIView] { - guard - let anchorView = taggerView.postHogAnchor, - let commonAncestor = anchorView.nearestCommonAncestor(with: taggerView) - else { - return [] - } - - return commonAncestor - .allDescendants(between: anchorView, and: taggerView) - .lazy - .filter { - // ignore some system SwiftUI views - !swiftUIIgnoreTypes.contains(where: $0.isKind(of:)) - } - .filter { - // exclude injected views - !$0.postHogView - } - } - - private struct PostHogTagAnchorView: UIViewRepresentable { - var id: UUID - - func makeUIView(context _: Context) -> some UIView { - PostHogTagAnchorUIView(id: id) - } - - func updateUIView(_: UIViewType, context _: Context) { - // - } - } - - private class PostHogTagAnchorUIView: UIView { - let id: UUID - - init(id: UUID) { - self.id = id - super.init(frame: .zero) - TaggingStore.shared[id, default: .init()].anchor = self - postHogView = true - } - - required init?(coder _: NSCoder) { - id = UUID() - super.init(frame: .zero) - } - } - - final class PostHogTagUIView: UIView { - let id: UUID - var handler: (() -> Void)? - - init( - id: UUID, - handler: ((PostHogTagUIView) -> Void)? - ) { - self.id = id - super.init(frame: .zero) - self.handler = { [weak self] in - guard let self else { - return - } - handler?(self) - } - - TaggingStore.shared[id, default: .init()].tagger = self - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - id = UUID() - super.init(frame: .zero) - } - - override func didMoveToSuperview() { - super.didMoveToSuperview() - postHogTagView = self - postHogView = true - handler?() - } - - override func didMoveToWindow() { - super.didMoveToWindow() - handler?() - } - - override func layoutSubviews() { - super.layoutSubviews() - handler?() - } - } - - private extension UIView { - var postHogTagView: PostHogTagUIView? { - get { objc_getAssociatedObject(self, &AssociatedKeys.phTagView) as? PostHogTagUIView } - set { objc_setAssociatedObject(self, &AssociatedKeys.phTagView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - - var postHogView: Bool { - get { objc_getAssociatedObject(self, &AssociatedKeys.phView) as? Bool ?? false } - set { objc_setAssociatedObject(self, &AssociatedKeys.phView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - } - - func allDescendants(between bottomEntity: UIView, and topEntity: UIView) -> some Sequence { - descendants - .lazy - .drop(while: { $0 !== bottomEntity }) - .prefix(while: { $0 !== topEntity }) - } - - var ancestors: some Sequence { - sequence(first: self, next: { $0.superview }).dropFirst() - } - - var descendants: some Sequence { - recursiveSequence([self], children: { $0.subviews }).dropFirst() - } - - func isDescendant(of other: UIView) -> Bool { - ancestors.contains(other) - } - - func nearestCommonAncestor(with other: UIView) -> UIView? { - var nearestAncestor: UIView? = self - - while let currentEntity = nearestAncestor, !other.isDescendant(of: currentEntity) { - nearestAncestor = currentEntity.superview - } - - return nearestAncestor - } - - var postHogAnchor: UIView? { - if let tagView = postHogTagView { - return TaggingStore.shared[tagView.id]?.anchor - } - return nil - } - } - - /** - A helper store for storing reference pairs between anchor and tagger views - */ - @MainActor private enum TaggingStore { - static var shared: [UUID: Pair] = [:] - - struct Pair { - weak var anchor: PostHogTagAnchorUIView? - weak var tagger: PostHogTagUIView? - } - } - - /** - Recursively iterates over a sequence of elements, applying a function to each element to get its children. - - - Parameters: - - sequence: The sequence of elements to iterate over. - - children: A function that takes an element and returns a sequence of its children. - - Returns: An AnySequence that iterates over all elements and their children. - */ - private func recursiveSequence(_ sequence: S, children: @escaping (S.Element) -> S) -> AnySequence { - AnySequence { - var mainIterator = sequence.makeIterator() - // Current iterator, or `nil` if all sequences are exhausted: - var iterator: AnyIterator? - - return AnyIterator { - guard let iterator, let element = iterator.next() else { - if let element = mainIterator.next() { - iterator = recursiveSequence(children(element), children: children).makeIterator() - return element - } - return nil - } - return element - } - } - } - - /** - Boxing a weak reference to a reference type. - */ - final class Weak { - weak var value: T? - - init(_ wrappedValue: T? = nil) { - value = wrappedValue - } - } - -#endif diff --git a/Pods/PostHog/PostHog/UIViewController.swift b/Pods/PostHog/PostHog/UIViewController.swift deleted file mode 100644 index 6def956..0000000 --- a/Pods/PostHog/PostHog/UIViewController.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// UIViewController.swift -// PostHog -// -// Inspired by -// https://raw.githubusercontent.com/segmentio/analytics-swift/e613e09aa1b97144126a923ec408374f914a6f2e/Examples/other_plugins/UIKitScreenTracking.swift -// -// Created by Manoel Aranda Neto on 23.10.23. -// - -#if os(iOS) || os(tvOS) - import Foundation - import UIKit - - extension UIViewController { - static func getViewControllerName(_ viewController: UIViewController) -> String? { - var title: String? = String(describing: viewController.classForCoder).replacingOccurrences(of: "ViewController", with: "") - - if title?.isEmpty == true { - title = viewController.title ?? nil - } - - return title - } - } -#endif diff --git a/Pods/PostHog/PostHog/Utils/AssociatedKeys.swift b/Pods/PostHog/PostHog/Utils/AssociatedKeys.swift deleted file mode 100644 index c8d0da6..0000000 --- a/Pods/PostHog/PostHog/Utils/AssociatedKeys.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// AssociatedKeys.swift -// PostHog -// -// Created by Yiannis Josephides on 04/12/2024. -// - -import Foundation - -enum AssociatedKeys { - static var phForwardingDelegate: UInt8 = 0 - static var phNoCapture: UInt8 = 0 - static var phNoMask: UInt8 = 0 - static var phTagView: UInt8 = 0 - static var phView: UInt8 = 0 - static var phLabel: UInt8 = 0 -} diff --git a/Pods/PostHog/PostHog/Utils/Data+Gzip.swift b/Pods/PostHog/PostHog/Utils/Data+Gzip.swift deleted file mode 100644 index 09498a6..0000000 --- a/Pods/PostHog/PostHog/Utils/Data+Gzip.swift +++ /dev/null @@ -1,301 +0,0 @@ -// -// Data+Gzip.swift -// -// https://github.com/1024jp/GzipSwift/blob/731037f6cc2be2ec01562f6597c1d0aa3fe6fd05/Sources/Gzip/Data%2BGzip.swift - -/* - The MIT License (MIT) - - © 2014-2023 1024jp - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ - -// issues importing scoped classes -// import struct Foundation.Data -import Foundation - -#if os(Linux) - import zlibLinux -#else - import zlib -#endif - -public enum Gzip { - /// Maximum value for windowBits (`MAX_WBITS`) - public static let maxWindowBits = MAX_WBITS -} - -/// Compression level whose rawValue is based on the zlib's constants. -public struct CompressionLevel: RawRepresentable, Sendable { - /// Compression level in the range of `0` (no compression) to `9` (maximum compression). - public let rawValue: Int32 - - public static let noCompression = Self(Z_NO_COMPRESSION) - public static let bestSpeed = Self(Z_BEST_SPEED) - public static let bestCompression = Self(Z_BEST_COMPRESSION) - - public static let defaultCompression = Self(Z_DEFAULT_COMPRESSION) - - public init(rawValue: Int32) { - self.rawValue = rawValue - } - - public init(_ rawValue: Int32) { - self.rawValue = rawValue - } -} - -/// Errors on gzipping/gunzipping based on the zlib error codes. -public struct GzipError: Swift.Error, Sendable { - // cf. http://www.zlib.net/manual.html - - public enum Kind: Equatable, Sendable { - /// The stream structure was inconsistent. - /// - /// - underlying zlib error: `Z_STREAM_ERROR` (-2) - case stream - - /// The input data was corrupted - /// (input stream not conforming to the zlib format or incorrect check value). - /// - /// - underlying zlib error: `Z_DATA_ERROR` (-3) - case data - - /// There was not enough memory. - /// - /// - underlying zlib error: `Z_MEM_ERROR` (-4) - case memory - - /// No progress is possible or there was not enough room in the output buffer. - /// - /// - underlying zlib error: `Z_BUF_ERROR` (-5) - case buffer - - /// The zlib library version is incompatible with the version assumed by the caller. - /// - /// - underlying zlib error: `Z_VERSION_ERROR` (-6) - case version - - /// An unknown error occurred. - /// - /// - parameter code: return error by zlib - case unknown(code: Int) - } - - /// Error kind. - public let kind: Kind - - /// Returned message by zlib. - public let message: String - - init(code: Int32, msg: UnsafePointer?) { - message = msg.flatMap(String.init(validatingUTF8:)) ?? "Unknown gzip error" - kind = Kind(code: code) - } - - public var localizedDescription: String { - message - } -} - -private extension GzipError.Kind { - init(code: Int32) { - switch code { - case Z_STREAM_ERROR: - self = .stream - case Z_DATA_ERROR: - self = .data - case Z_MEM_ERROR: - self = .memory - case Z_BUF_ERROR: - self = .buffer - case Z_VERSION_ERROR: - self = .version - default: - self = .unknown(code: Int(code)) - } - } -} - -extension Data { - /// Whether the receiver is compressed in gzip format. - var isGzipped: Bool { - starts(with: [0x1F, 0x8B]) // check magic number - } - - /// Create a new `Data` instance by compressing the receiver using zlib. - /// Throws an error if compression failed. - /// - /// The `wBits` parameter allows for managing the size of the history buffer. The possible values are: - /// - /// Value Window size logarithm Input - /// +9 to +15 Base 2 Includes zlib header and trailer - /// -9 to -15 Absolute value of wbits No header and trailer - /// +25 to +31 Low 4 bits of the value Includes gzip header and trailing checksum - /// - /// - Parameter level: Compression level. - /// - Parameter wBits: Manage the size of the history buffer. - /// - Returns: Gzip-compressed `Data` instance. - /// - Throws: `GzipError` - func gzipped(level: CompressionLevel = .defaultCompression, wBits: Int32 = Gzip.maxWindowBits + 16) throws -> Data { - guard !isEmpty else { - return Data() - } - - var stream = z_stream() - var status: Int32 - - status = deflateInit2_(&stream, level.rawValue, Z_DEFLATED, wBits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY, ZLIB_VERSION, Int32(DataSize.stream)) - - guard status == Z_OK else { - // deflateInit2 returns: - // Z_VERSION_ERROR The zlib library version is incompatible with the version assumed by the caller. - // Z_MEM_ERROR There was not enough memory. - // Z_STREAM_ERROR A parameter is invalid. - - throw GzipError(code: status, msg: stream.msg) - } - - var data = Data(capacity: DataSize.chunk) - repeat { - if Int(stream.total_out) >= data.count { - data.count += DataSize.chunk - } - - let inputCount = count - let outputCount = data.count - - withUnsafeBytes { (inputPointer: UnsafeRawBufferPointer) in - stream.next_in = UnsafeMutablePointer(mutating: inputPointer.bindMemory(to: Bytef.self).baseAddress!).advanced(by: Int(stream.total_in)) - stream.avail_in = uInt(inputCount) - uInt(stream.total_in) - - data.withUnsafeMutableBytes { (outputPointer: UnsafeMutableRawBufferPointer) in - stream.next_out = outputPointer.bindMemory(to: Bytef.self).baseAddress!.advanced(by: Int(stream.total_out)) - stream.avail_out = uInt(outputCount) - uInt(stream.total_out) - - status = deflate(&stream, Z_FINISH) - - stream.next_out = nil - } - - stream.next_in = nil - } - - } while stream.avail_out == 0 && status != Z_STREAM_END - - guard deflateEnd(&stream) == Z_OK, status == Z_STREAM_END else { - throw GzipError(code: status, msg: stream.msg) - } - - data.count = Int(stream.total_out) - - return data - } - - /// Create a new `Data` instance by decompressing the receiver using zlib. - /// Throws an error if decompression failed. - /// - /// The `wBits` parameter allows for managing the size of the history buffer. The possible values are: - /// - /// Value Window size logarithm Input - /// +8 to +15 Base 2 Includes zlib header and trailer - /// -8 to -15 Absolute value of wbits Raw stream with no header and trailer - /// +24 to +31 = 16 + (8 to 15) Low 4 bits of the value Includes gzip header and trailer - /// +40 to +47 = 32 + (8 to 15) Low 4 bits of the value zlib or gzip format - /// - /// - Parameter wBits: Manage the size of the history buffer. - /// - Returns: Gzip-decompressed `Data` instance. - /// - Throws: `GzipError` - func gunzipped(wBits: Int32 = Gzip.maxWindowBits + 32) throws -> Data { - guard !isEmpty else { - return Data() - } - - var data = Data(capacity: count * 2) - var totalIn: uLong = 0 - var totalOut: uLong = 0 - - repeat { - var stream = z_stream() - var status: Int32 - - status = inflateInit2_(&stream, wBits, ZLIB_VERSION, Int32(DataSize.stream)) - - guard status == Z_OK else { - // inflateInit2 returns: - // Z_VERSION_ERROR The zlib library version is incompatible with the version assumed by the caller. - // Z_MEM_ERROR There was not enough memory. - // Z_STREAM_ERROR A parameters are invalid. - - throw GzipError(code: status, msg: stream.msg) - } - - repeat { - if Int(totalOut + stream.total_out) >= data.count { - data.count += count / 2 - } - - let inputCount = count - let outputCount = data.count - - withUnsafeBytes { (inputPointer: UnsafeRawBufferPointer) in - let inputStartPosition = totalIn + stream.total_in - stream.next_in = UnsafeMutablePointer(mutating: inputPointer.bindMemory(to: Bytef.self).baseAddress!).advanced(by: Int(inputStartPosition)) - stream.avail_in = uInt(inputCount) - uInt(inputStartPosition) - - data.withUnsafeMutableBytes { (outputPointer: UnsafeMutableRawBufferPointer) in - let outputStartPosition = totalOut + stream.total_out - stream.next_out = outputPointer.bindMemory(to: Bytef.self).baseAddress!.advanced(by: Int(outputStartPosition)) - stream.avail_out = uInt(outputCount) - uInt(outputStartPosition) - - status = inflate(&stream, Z_SYNC_FLUSH) - - stream.next_out = nil - } - - stream.next_in = nil - } - } while status == Z_OK - - totalIn += stream.total_in - - guard inflateEnd(&stream) == Z_OK, status == Z_STREAM_END else { - // inflate returns: - // Z_DATA_ERROR The input data was corrupted (input stream not conforming to the zlib format or incorrect check value). - // Z_STREAM_ERROR The stream structure was inconsistent (for example if next_in or next_out was NULL). - // Z_MEM_ERROR There was not enough memory. - // Z_BUF_ERROR No progress is possible or there was not enough room in the output buffer when Z_FINISH is used. - throw GzipError(code: status, msg: stream.msg) - } - - totalOut += stream.total_out - - } while totalIn < count - - data.count = Int(totalOut) - - return data - } -} - -private enum DataSize { - static let chunk = 1 << 14 - static let stream = MemoryLayout.size -} diff --git a/Pods/PostHog/PostHog/Utils/DateUtils.swift b/Pods/PostHog/PostHog/Utils/DateUtils.swift deleted file mode 100644 index f874b1b..0000000 --- a/Pods/PostHog/PostHog/Utils/DateUtils.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// DateUtils.swift -// PostHog -// -// Created by Manoel Aranda Neto on 27.10.23. -// - -import Foundation - -final class PostHogAPIDateFormatter { - private static func getFormatter(with format: String) -> DateFormatter { - let dateFormatter = DateFormatter() - dateFormatter.locale = Locale(identifier: "en_US_POSIX") - dateFormatter.timeZone = TimeZone(abbreviation: "UTC") - dateFormatter.dateFormat = format - return dateFormatter - } - - private let dateFormatterWithMilliseconds = getFormatter(with: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") - - private let dateFormatterWithSeconds = getFormatter(with: "yyyy-MM-dd'T'HH:mm:ss'Z'") - - func string(from date: Date) -> String { - dateFormatterWithMilliseconds.string(from: date) - } - - func date(from string: String) -> Date? { - dateFormatterWithMilliseconds.date(from: string) - ?? dateFormatterWithSeconds.date(from: string) - } -} - -let apiDateFormatter = PostHogAPIDateFormatter() - -public func toISO8601String(_ date: Date) -> String { - apiDateFormatter.string(from: date) -} - -public func toISO8601Date(_ date: String) -> Date? { - apiDateFormatter.date(from: date) -} - -var now: () -> Date = { Date() } diff --git a/Pods/PostHog/PostHog/Utils/DictUtils.swift b/Pods/PostHog/PostHog/Utils/DictUtils.swift deleted file mode 100644 index e0d0612..0000000 --- a/Pods/PostHog/PostHog/Utils/DictUtils.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// DictUtils.swift -// PostHog -// -// Created by Manoel Aranda Neto on 27.10.23. -// - -import Foundation - -public func sanitizeDictionary(_ dict: [String: Any]?) -> [String: Any]? { - if dict == nil || dict!.isEmpty { - return nil - } - - var newDict = dict! - - for (key, value) in newDict where !isValidObject(value) { - if value is URL { - newDict[key] = (value as! URL).absoluteString - continue - } - if value is Date { - newDict[key] = ISO8601DateFormatter().string(from: (value as! Date)) - continue - } - - newDict.removeValue(forKey: key) - hedgeLog("property: \(key) isn't serializable, dropping the item") - } - - return newDict -} - -private func isValidObject(_ object: Any) -> Bool { - if object is String || object is Bool || object is any Numeric || object is NSNumber { - return true - } - if object is [Any?] || object is [String: Any?] { - return JSONSerialization.isValidJSONObject(object) - } - // workaround [object] since isValidJSONObject only accepts an Array or Dict - return JSONSerialization.isValidJSONObject([object]) -} diff --git a/Pods/PostHog/PostHog/Utils/Errors.swift b/Pods/PostHog/PostHog/Utils/Errors.swift deleted file mode 100644 index 35eb31c..0000000 --- a/Pods/PostHog/PostHog/Utils/Errors.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// Errors.swift -// PostHog -// -// Created by Ben White on 21.03.23. -// - -import Foundation - -struct InternalPostHogError: Error, CustomStringConvertible { - let description: String - - init(description: String, fileID: StaticString = #fileID, line: UInt = #line) { - self.description = "\(description) (\(fileID):\(line))" - } -} diff --git a/Pods/PostHog/PostHog/Utils/FileUtils.swift b/Pods/PostHog/PostHog/Utils/FileUtils.swift deleted file mode 100644 index 1bb9b20..0000000 --- a/Pods/PostHog/PostHog/Utils/FileUtils.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// FileUtils.swift -// PostHog -// -// Created by Manoel Aranda Neto on 30.10.23. -// - -import Foundation - -public func deleteSafely(_ file: URL) { - if FileManager.default.fileExists(atPath: file.path) { - do { - try FileManager.default.removeItem(at: file) - } catch { - hedgeLog("Error trying to delete file \(file.path) \(error)") - } - } -} - -/// Check if provided directory exists -func directoryExists(_ directory: URL) -> Bool { - var isDirectory: ObjCBool = false - return FileManager.default.fileExists(atPath: directory.path, isDirectory: &isDirectory) && isDirectory.boolValue -} - -func createDirectoryAtURLIfNeeded(url: URL) { - if FileManager.default.fileExists(atPath: url.path) { return } - do { - try FileManager.default.createDirectory(atPath: url.path, withIntermediateDirectories: true) - } catch { - hedgeLog("Error creating storage directory: \(error)") - } -} diff --git a/Pods/PostHog/PostHog/Utils/Hedgelog.swift b/Pods/PostHog/PostHog/Utils/Hedgelog.swift deleted file mode 100644 index f53ad86..0000000 --- a/Pods/PostHog/PostHog/Utils/Hedgelog.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Hedgelog.swift -// PostHog -// -// Created by Ben White on 07.02.23. -// - -import Foundation - -var hedgeLogEnabled = false - -func toggleHedgeLog(_ enabled: Bool) { - hedgeLogEnabled = enabled -} - -// Meant for internally logging PostHog related things -func hedgeLog(_ message: String) { - if !hedgeLogEnabled { return } - print("[PostHog] \(message)") -} diff --git a/Pods/PostHog/PostHog/Utils/Reachability.swift b/Pods/PostHog/PostHog/Utils/Reachability.swift deleted file mode 100644 index fe840e8..0000000 --- a/Pods/PostHog/PostHog/Utils/Reachability.swift +++ /dev/null @@ -1,406 +0,0 @@ -/* - Copyright (c) 2014, Ashley Mills - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - */ - -import Foundation - -#if !os(watchOS) - import SystemConfiguration - - public enum ReachabilityError: Error { - case failedToCreateWithAddress(sockaddr, Int32) - case failedToCreateWithHostname(String, Int32) - case unableToSetCallback(Int32) - case unableToSetDispatchQueue(Int32) - case unableToGetFlags(Int32) - } - - @available(*, unavailable, renamed: "Notification.Name.reachabilityChanged") - public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification") - - extension Notification.Name { - static let reachabilityChanged = Notification.Name("reachabilityChanged") - } - - public class Reachability { - public typealias NetworkReachable = (Reachability) -> Void - public typealias NetworkUnreachable = (Reachability) -> Void - - @available(*, unavailable, renamed: "Connection") - public enum NetworkStatus: CustomStringConvertible { - case notReachable, reachableViaWiFi, reachableViaWWAN - public var description: String { - switch self { - case .reachableViaWWAN: return "Cellular" - case .reachableViaWiFi: return "WiFi" - case .notReachable: return "No Connection" - } - } - } - - public enum Connection: CustomStringConvertible { - case unavailable, wifi, cellular - public var description: String { - switch self { - case .cellular: return "Cellular" - case .wifi: return "WiFi" - case .unavailable: return "No Connection" - } - } - - @available(*, deprecated, renamed: "unavailable") - public static let none: Connection = .unavailable - } - - public var whenReachable: NetworkReachable? - public var whenUnreachable: NetworkUnreachable? - - @available(*, deprecated, renamed: "allowsCellularConnection") - public let reachableOnWWAN: Bool = true - - /// Set to `false` to force Reachability.connection to .none when on cellular connection (default value `true`) - public var allowsCellularConnection: Bool - - // The notification center on which "reachability changed" events are being posted - public var notificationCenter: NotificationCenter = .default - - @available(*, deprecated, renamed: "connection.description") - public var currentReachabilityString: String { - "\(connection)" - } - - @available(*, unavailable, renamed: "connection") - public var currentReachabilityStatus: Connection { - connection - } - - public var connection: Connection { - if flags == nil { - try? setReachabilityFlags() - } - - switch flags?.connection { - case .unavailable?, nil: return .unavailable - case .cellular?: return allowsCellularConnection ? .cellular : .unavailable - case .wifi?: return .wifi - } - } - - private(set) var notifierRunning = false - private let reachabilityRef: SCNetworkReachability - private let reachabilitySerialQueue: DispatchQueue - private let notificationQueue: DispatchQueue? - private(set) var flags: SCNetworkReachabilityFlags? { - didSet { - guard flags != oldValue else { return } - notifyReachabilityChanged() - } - } - - public required init(reachabilityRef: SCNetworkReachability, - queueQoS: DispatchQoS = .default, - targetQueue: DispatchQueue? = nil, - notificationQueue: DispatchQueue? = .main) - { - allowsCellularConnection = true - self.reachabilityRef = reachabilityRef - reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability", qos: queueQoS, target: targetQueue) - self.notificationQueue = notificationQueue - } - - public convenience init(hostname: String, - queueQoS: DispatchQoS = .default, - targetQueue: DispatchQueue? = nil, - notificationQueue: DispatchQueue? = .main) throws - { - guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { - throw ReachabilityError.failedToCreateWithHostname(hostname, SCError()) - } - self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue, notificationQueue: notificationQueue) - } - - public convenience init(queueQoS: DispatchQoS = .default, - targetQueue: DispatchQueue? = nil, - notificationQueue: DispatchQueue? = .main) throws - { - var zeroAddress = sockaddr() - zeroAddress.sa_len = UInt8(MemoryLayout.size) - zeroAddress.sa_family = sa_family_t(AF_INET) - - guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else { - throw ReachabilityError.failedToCreateWithAddress(zeroAddress, SCError()) - } - - self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue, notificationQueue: notificationQueue) - } - - deinit { - stopNotifier() - } - } - - extension Reachability { - // MARK: - *** Notifier methods *** - - func startNotifier() throws { - guard !notifierRunning else { return } - - let callback: SCNetworkReachabilityCallBack = { _, flags, info in - guard let info = info else { return } - - // `weakifiedReachability` is guaranteed to exist by virtue of our - // retain/release callbacks which we provided to the `SCNetworkReachabilityContext`. - let weakifiedReachability = Unmanaged.fromOpaque(info).takeUnretainedValue() - - // The weak `reachability` _may_ no longer exist if the `Reachability` - // object has since been deallocated but a callback was already in flight. - weakifiedReachability.reachability?.flags = flags - } - - let weakifiedReachability = ReachabilityWeakifier(reachability: self) - let opaqueWeakifiedReachability = Unmanaged.passUnretained(weakifiedReachability).toOpaque() - - var context = SCNetworkReachabilityContext( - version: 0, - info: UnsafeMutableRawPointer(opaqueWeakifiedReachability), - retain: { (info: UnsafeRawPointer) -> UnsafeRawPointer in - let unmanagedWeakifiedReachability = Unmanaged.fromOpaque(info) - _ = unmanagedWeakifiedReachability.retain() - return UnsafeRawPointer(unmanagedWeakifiedReachability.toOpaque()) - }, - release: { (info: UnsafeRawPointer) in - let unmanagedWeakifiedReachability = Unmanaged.fromOpaque(info) - unmanagedWeakifiedReachability.release() - }, - copyDescription: { (info: UnsafeRawPointer) -> Unmanaged in - let unmanagedWeakifiedReachability = Unmanaged.fromOpaque(info) - let weakifiedReachability = unmanagedWeakifiedReachability.takeUnretainedValue() - let description = weakifiedReachability.reachability?.description ?? "nil" - return Unmanaged.passRetained(description as CFString) - } - ) - - if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) { - stopNotifier() - throw ReachabilityError.unableToSetCallback(SCError()) - } - - if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) { - stopNotifier() - throw ReachabilityError.unableToSetDispatchQueue(SCError()) - } - - // Perform an initial check - try setReachabilityFlags() - - notifierRunning = true - } - - func stopNotifier() { - defer { notifierRunning = false } - - SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) - SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil) - } - - // MARK: - *** Connection test methods *** - - @available(*, deprecated, message: "Please use `connection != .none`") - var isReachable: Bool { - connection != .unavailable - } - - @available(*, deprecated, message: "Please use `connection == .cellular`") - var isReachableViaWWAN: Bool { - // Check we're not on the simulator, we're REACHABLE and check we're on WWAN - connection == .cellular - } - - @available(*, deprecated, message: "Please use `connection == .wifi`") - var isReachableViaWiFi: Bool { - connection == .wifi - } - - var description: String { - flags?.description ?? "unavailable flags" - } - } - - private extension Reachability { - func setReachabilityFlags() throws { - try reachabilitySerialQueue.sync { [unowned self] in - var flags = SCNetworkReachabilityFlags() - if !SCNetworkReachabilityGetFlags(reachabilityRef, &flags) { - stopNotifier() - throw ReachabilityError.unableToGetFlags(SCError()) - } - - self.flags = flags - } - } - - func notifyReachabilityChanged() { - let notify = { [weak self] in - guard let self = self else { return } - self.connection != .unavailable ? self.whenReachable?(self) : self.whenUnreachable?(self) - self.notificationCenter.post(name: .reachabilityChanged, object: self) - } - - // notify on the configured `notificationQueue`, or the caller's (i.e. `reachabilitySerialQueue`) - notificationQueue?.async(execute: notify) ?? notify() - } - } - - extension SCNetworkReachabilityFlags { - typealias Connection = Reachability.Connection - - var connection: Connection { - guard isReachableFlagSet else { return .unavailable } - - // If we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi - #if targetEnvironment(simulator) - return .wifi - #else - var connection = Connection.unavailable - - if !isConnectionRequiredFlagSet { - connection = .wifi - } - - if isConnectionOnTrafficOrDemandFlagSet { - if !isInterventionRequiredFlagSet { - connection = .wifi - } - } - - if isOnWWANFlagSet { - connection = .cellular - } - - return connection - #endif - } - - var isOnWWANFlagSet: Bool { - #if os(iOS) || os(visionOS) - return contains(.isWWAN) - #else - return false - #endif - } - - var isReachableFlagSet: Bool { - contains(.reachable) - } - - var isConnectionRequiredFlagSet: Bool { - contains(.connectionRequired) - } - - var isInterventionRequiredFlagSet: Bool { - contains(.interventionRequired) - } - - var isConnectionOnTrafficFlagSet: Bool { - contains(.connectionOnTraffic) - } - - var isConnectionOnDemandFlagSet: Bool { - contains(.connectionOnDemand) - } - - var isConnectionOnTrafficOrDemandFlagSet: Bool { - !intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty - } - - var isTransientConnectionFlagSet: Bool { - contains(.transientConnection) - } - - var isLocalAddressFlagSet: Bool { - contains(.isLocalAddress) - } - - var isDirectFlagSet: Bool { - contains(.isDirect) - } - - var description: String { - let W = isOnWWANFlagSet ? "W" : "-" - let R = isReachableFlagSet ? "R" : "-" - let c = isConnectionRequiredFlagSet ? "c" : "-" - let t = isTransientConnectionFlagSet ? "t" : "-" - let i = isInterventionRequiredFlagSet ? "i" : "-" - let C = isConnectionOnTrafficFlagSet ? "C" : "-" - let D = isConnectionOnDemandFlagSet ? "D" : "-" - let l = isLocalAddressFlagSet ? "l" : "-" - let d = isDirectFlagSet ? "d" : "-" - - return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" - } - } - - /** - `ReachabilityWeakifier` weakly wraps the `Reachability` class - in order to break retain cycles when interacting with CoreFoundation. - - CoreFoundation callbacks expect a pair of retain/release whenever an - opaque `info` parameter is provided. These callbacks exist to guard - against memory management race conditions when invoking the callbacks. - - #### Race Condition - - If we passed `SCNetworkReachabilitySetCallback` a direct reference to our - `Reachability` class without also providing corresponding retain/release - callbacks, then a race condition can lead to crashes when: - - `Reachability` is deallocated on thread X - - A `SCNetworkReachability` callback(s) is already in flight on thread Y - - #### Retain Cycle - - If we pass `Reachability` to CoreFoundtion while also providing retain/ - release callbacks, we would create a retain cycle once CoreFoundation - retains our `Reachability` class. This fixes the crashes and his how - CoreFoundation expects the API to be used, but doesn't play nicely with - Swift/ARC. This cycle would only be broken after manually calling - `stopNotifier()` — `deinit` would never be called. - - #### ReachabilityWeakifier - - By providing both retain/release callbacks and wrapping `Reachability` in - a weak wrapper, we: - - interact correctly with CoreFoundation, thereby avoiding a crash. - See "Memory Management Programming Guide for Core Foundation". - - don't alter the public API of `Reachability.swift` in any way - - still allow for automatic stopping of the notifier on `deinit`. - */ - private class ReachabilityWeakifier { - weak var reachability: Reachability? - init(reachability: Reachability) { - self.reachability = reachability - } - } -#endif diff --git a/Pods/PostHog/PostHog/Utils/ReadWriteLock.swift b/Pods/PostHog/PostHog/Utils/ReadWriteLock.swift deleted file mode 100644 index c782a15..0000000 --- a/Pods/PostHog/PostHog/Utils/ReadWriteLock.swift +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2019-Present Datadog, Inc. - */ - -import Foundation - -/// A property wrapper using a fair, POSIX conforming reader-writer lock for atomic -/// access to the value. It is optimised for concurrent reads and exclusive writes. -/// -/// The wrapper is a class to prevent copying the lock, it creates and initilaizes a `pthread_rwlock_t`. -/// An additional method `mutate` allow to safely mutate the value in-place (to read it -/// and write it while obtaining the lock only once). -@propertyWrapper -public final class ReadWriteLock { - /// The wrapped value. - private var value: Value - - /// The lock object. - private var rwlock = pthread_rwlock_t() - - public init(wrappedValue value: Value) { - pthread_rwlock_init(&rwlock, nil) - self.value = value - } - - deinit { - pthread_rwlock_destroy(&rwlock) - } - - /// The wrapped value. - /// - /// The `get` will acquire the lock for reading while the `set` will acquire for - /// writing. - public var wrappedValue: Value { - get { - pthread_rwlock_rdlock(&rwlock) - defer { pthread_rwlock_unlock(&rwlock) } - return value - } - set { - pthread_rwlock_wrlock(&rwlock) - value = newValue - pthread_rwlock_unlock(&rwlock) - } - } - - /// Provides a non-escaping closure for mutation. - /// The lock will be acquired once for writing before invoking the closure. - /// - /// - Parameter closure: The closure with the mutable value. - @discardableResult - public func mutate(_ closure: (inout Value) -> T) -> T { - pthread_rwlock_wrlock(&rwlock) - defer { - pthread_rwlock_unlock(&rwlock) - } - return closure(&value) - } -} - -func synchronized(_ lock: Any, closure: () -> Void) { - objc_sync_enter(lock) - closure() - objc_sync_exit(lock) -} diff --git a/Pods/PostHog/PostHog/Utils/TimeBasedEpochGenerator.swift b/Pods/PostHog/PostHog/Utils/TimeBasedEpochGenerator.swift deleted file mode 100644 index 5c6c159..0000000 --- a/Pods/PostHog/PostHog/Utils/TimeBasedEpochGenerator.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// TimeBasedEpochGenerator.swift -// PostHog -// -// Created by Manoel Aranda Neto on 17.06.24. -// - -import Foundation - -class TimeBasedEpochGenerator { - static let shared = TimeBasedEpochGenerator() - - // Private initializer to prevent multiple instances - private init() {} - - private var lastEntropy = [UInt8](repeating: 0, count: 10) - private var lastTimestamp: UInt64 = 0 - - private let lock = NSLock() - - func v7() -> UUID { - var uuid: UUID? - - lock.withLock { - uuid = generateUUID() - } - - // or fallback to UUID v4 - return uuid ?? UUID() - } - - private func generateUUID() -> UUID? { - let timestamp = Date().timeIntervalSince1970 - let unixTimeMilliseconds = UInt64(timestamp * 1000) - - var uuidBytes = [UInt8]() - - let timeBytes = unixTimeMilliseconds.bigEndianData.suffix(6) // First 6 bytes for the timestamp - uuidBytes.append(contentsOf: timeBytes) - - if unixTimeMilliseconds == lastTimestamp { - var check = true - for index in (0 ..< 10).reversed() where check { - var temp = lastEntropy[index] - temp = temp &+ 0x01 - check = lastEntropy[index] == 0xFF - lastEntropy[index] = temp - } - } else { - lastTimestamp = unixTimeMilliseconds - - // Prepare the random part (10 bytes to complete the UUID) - let status = SecRandomCopyBytes(kSecRandomDefault, lastEntropy.count, &lastEntropy) - // If we can't generate secure random bytes, use a fallback - if status != errSecSuccess { - let randomData = (0 ..< 10).map { _ in UInt8.random(in: 0 ... 255) } - lastEntropy = randomData - } - } - uuidBytes.append(contentsOf: lastEntropy) - - // Set version (7) in the version byte - uuidBytes[6] = (uuidBytes[6] & 0x0F) | 0x70 - - // Set the UUID variant (10xx for standard UUIDs) - uuidBytes[8] = (uuidBytes[8] & 0x3F) | 0x80 - - // Ensure we have a total of 16 bytes - if uuidBytes.count == 16 { - return UUID(uuid: (uuidBytes[0], uuidBytes[1], uuidBytes[2], uuidBytes[3], - uuidBytes[4], uuidBytes[5], uuidBytes[6], uuidBytes[7], - uuidBytes[8], uuidBytes[9], uuidBytes[10], uuidBytes[11], - uuidBytes[12], uuidBytes[13], uuidBytes[14], uuidBytes[15])) - } - - return nil - } -} - -extension UInt64 { - // Correctly generate Data representation in big endian format - var bigEndianData: Data { - var bigEndianValue = bigEndian - return Data(bytes: &bigEndianValue, count: MemoryLayout.size) - } -} diff --git a/Pods/PostHog/PostHog/Utils/UIApplication+.swift b/Pods/PostHog/PostHog/Utils/UIApplication+.swift deleted file mode 100644 index afe9b2b..0000000 --- a/Pods/PostHog/PostHog/Utils/UIApplication+.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// UIApplication+.swift -// PostHog -// -// Created by Yiannis Josephides on 11/11/2024. -// - -#if os(iOS) || os(tvOS) || os(visionOS) - import UIKit - - extension UIApplication { - static func getCurrentWindow(filterForegrounded: Bool = true) -> UIWindow? { - let windowScenes = UIApplication.shared - .connectedScenes - .compactMap { $0 as? UIWindowScene } - .filter { - !filterForegrounded || $0.activationState == .foregroundActive - } - - for scene in windowScenes { - // attempt to retrieve directly from UIWindowScene - if #available(iOS 15.0, tvOS 15.0, *) { - if let keyWindow = scene.keyWindow { - return keyWindow - } - } else { - // check scene.windows.isKeyWindow - for window in scene.windows where window.isKeyWindow { - return window - } - } - - // check scene.delegate.window property - let sceneDelegate = scene.delegate as? UIWindowSceneDelegate - if let target = sceneDelegate, let window = target.window { - return window - } - } - - return nil - } - } -#endif diff --git a/Pods/PostHog/PostHog/Utils/UIImage+WebP.swift b/Pods/PostHog/PostHog/Utils/UIImage+WebP.swift deleted file mode 100644 index 377f0bd..0000000 --- a/Pods/PostHog/PostHog/Utils/UIImage+WebP.swift +++ /dev/null @@ -1,138 +0,0 @@ -// -// UIImage+WebP.swift -// PostHog -// -// Created by Yiannis Josephides on 09/12/2024. -// -// Adapted from: https://github.com/SDWebImage/SDWebImageWebPCoder/blob/master/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m - -#if os(iOS) - import Accelerate - import CoreGraphics - import Foundation - #if canImport(phlibwebp) - // SPM package is linked via a lib since mix-code is not yet supported - - // `internal import`: added in Swift 5.9 and it's the "official" feature. Should replace when we switch to swift-tools-version:5.9 - // see: (https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md) - - // @_implementationOnly: available since Swift 5.1 - @_implementationOnly import phlibwebp - #endif - import UIKit - - extension UIImage { - /** - Returns a data object that contains the image in WebP format. - - - Parameters: - - compressionQuality: desired compression quality [0...1] (0=max/lowest quality, 1=low/high quality) - - Returns: A data object containing the WebP data, or nil if there’s a problem generating the data. - */ - func webpData(compressionQuality: CGFloat) -> Data? { - // Early exit if image is missing - guard let cgImage = cgImage else { - return nil - } - - // validate dimensions - let width = Int(cgImage.width) - let height = Int(cgImage.height) - - guard width > 0, width <= WEBP_MAX_DIMENSION, height > 0, height <= WEBP_MAX_DIMENSION else { - return nil - } - - let bitmapInfo = cgImage.bitmapInfo - let alphaInfo = CGImageAlphaInfo(rawValue: bitmapInfo.rawValue & CGBitmapInfo.alphaInfoMask.rawValue) - - // Prepare destination format - - let hasAlpha = !( - alphaInfo == CGImageAlphaInfo.none || - alphaInfo == CGImageAlphaInfo.noneSkipFirst || - alphaInfo == CGImageAlphaInfo.noneSkipLast - ) - - // try to use image color space if ~rgb - let colorSpace: CGColorSpace = cgImage.colorSpace?.model == .rgb - ? cgImage.colorSpace! // safe from previous check - : CGColorSpace(name: CGColorSpace.linearSRGB)! - let renderingIntent = cgImage.renderingIntent - - guard let destFormat = vImage_CGImageFormat( - bitsPerComponent: 8, - bitsPerPixel: hasAlpha ? 32 : 24, // RGB888/RGBA8888 - colorSpace: colorSpace, - bitmapInfo: hasAlpha - ? CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue | CGBitmapInfo.byteOrderDefault.rawValue) - : CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue | CGBitmapInfo.byteOrderDefault.rawValue), - renderingIntent: renderingIntent - ) else { - return nil - } - - guard let dest = try? vImage_Buffer(cgImage: cgImage, format: destFormat, flags: .noFlags) else { - hedgeLog("Error initializing WebP image buffer") - return nil - } - defer { dest.data?.deallocate() } - - guard let rgba = dest.data else { // byte array - hedgeLog("Could not get rgba byte array from destination format") - return nil - } - let bytesPerRow = dest.rowBytes - - let quality = Float(compressionQuality * 100) // WebP quality is 0-100 - - var config = WebPConfig() - var picture = WebPPicture() - var writer = WebPMemoryWriter() - - // get present... - guard WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality) != 0, WebPPictureInit(&picture) != 0 else { - hedgeLog("Error initializing WebPPicture") - return nil - } - - withUnsafeMutablePointer(to: &writer) { writerPointer in - picture.use_argb = 1 // Lossy encoding uses YUV for internal bitstream - picture.width = Int32(width) - picture.height = Int32(height) - picture.writer = WebPMemoryWrite - picture.custom_ptr = UnsafeMutableRawPointer(writerPointer) - } - - WebPMemoryWriterInit(&writer) - - defer { - WebPMemoryWriterClear(&writer) - WebPPictureFree(&picture) - } - - let result: Int32 - if hasAlpha { - // RGBA8888 - 4 channels - result = WebPPictureImportRGBA(&picture, rgba.bindMemory(to: UInt8.self, capacity: 4), Int32(bytesPerRow)) - } else { - // RGB888 - 3 channels - result = WebPPictureImportRGB(&picture, rgba.bindMemory(to: UInt8.self, capacity: 3), Int32(bytesPerRow)) - } - - if result == 0 { - hedgeLog("Could not read WebPPicture") - return nil - } - - if WebPEncode(&config, &picture) == 0 { - hedgeLog("Could not encode WebP image") - return nil - } - - let webpData = Data(bytes: writer.mem, count: writer.size) - - return webpData - } - } -#endif diff --git a/Pods/PostHog/PostHog/Utils/UIWindow+.swift b/Pods/PostHog/PostHog/Utils/UIWindow+.swift deleted file mode 100644 index f119708..0000000 --- a/Pods/PostHog/PostHog/Utils/UIWindow+.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// UIWindow+.swift -// PostHog -// -// Created by Yiannis Josephides on 03/12/2024. -// - -#if os(iOS) || os(tvOS) - import Foundation - import UIKit - - /** - Known keyboard window (private) types - - ## UIRemoteKeyboardWindow - This is the window that manages the actual keyboard - - The following system view controllers were observed to be presented in a UIRemoteKeyboardWindow window - - UIInputWindowController - - UICompatibilityInputViewController - - UISystemInputAssistantViewController - - UIPredictionViewController - - UISystemKeyboardDockController - - TUIEmojiSearchInputViewController - - STKPrewarmingViewController - - STKStickerRemoteSearchViewController - - _UIRemoteInputViewController - - _UISceneHostingViewController - - STKEmojiAndStickerCollectionViewController - - ## UITextEffectsWindow - Hosts system components like the magnifying glass for text selection, predictive text suggestions, copy/paste menus, input accessory views etc. - - The following system view controllers were observed to be presented in a UITextEffectsWindow window - - UIInputWindowController - - UICompatibilityInputViewController - - These view controllers should not appear in a $screen event. If they do, then it means that they are presented in a UIWindow not listed below - */ - private let knownKeyboardWindowTypes: [String] = [ - "UIRemoteKeyboardWindow", - "UITextEffectsWindow", - ] - - extension UIWindow { - var isKeyboardWindow: Bool { - knownKeyboardWindowTypes.contains(String(describing: type(of: self))) - } - } -#endif diff --git a/Pods/PostHog/PostHog/Utils/UUIDUtils.swift b/Pods/PostHog/PostHog/Utils/UUIDUtils.swift deleted file mode 100644 index 66c3dd2..0000000 --- a/Pods/PostHog/PostHog/Utils/UUIDUtils.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// UUIDUtils.swift -// PostHog -// -// Created by Manoel Aranda Neto on 17.06.24. -// - -// Inspired and adapted from https://github.com/nthState/UUIDV7/blob/main/Sources/UUIDV7/UUIDV7.swift -// but using SecRandomCopyBytes - -import Foundation - -extension UUID { - static func v7() -> Self { - TimeBasedEpochGenerator.shared.v7() - } -} diff --git a/Pods/PostHog/README.md b/Pods/PostHog/README.md deleted file mode 100644 index 2be037b..0000000 --- a/Pods/PostHog/README.md +++ /dev/null @@ -1,15 +0,0 @@ -[![Build](https://img.shields.io/github/actions/workflow/status/PostHog/posthog-ios/build.yml?branch=main)](https://github.com/PostHog/posthog-ios/actions/workflows/build.yml?query=branch%3Amain) -[![CocoaPods compadible](https://img.shields.io/cocoapods/v/PostHog.svg)](https://cocoapods.org/pods/PostHog) -[![SwiftPM compatible](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager) -![platforms](https://img.shields.io/cocoapods/p/PostHog.svg?style=flat) -[![Swift Package Index](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FPostHog%2Fposthog-ios%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/PostHog/posthog-ios) - -# PostHog iOS - -Please see the main [PostHog docs](https://posthog.com/docs). - -Specifically, the [iOS docs](https://posthog.com/docs/libraries/ios) details. - -## Questions? - -### [Check out our community page.](https://posthog.com/posts) diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-Info.plist b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-Info.plist deleted file mode 100644 index 19cf209..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - ${PODS_DEVELOPMENT_LANGUAGE} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-acknowledgements.markdown b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-acknowledgements.markdown deleted file mode 100644 index b81fb24..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-acknowledgements.markdown +++ /dev/null @@ -1,28 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## PostHog - -MIT License - -Copyright (c) [2023] [PostHog] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-acknowledgements.plist b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-acknowledgements.plist deleted file mode 100644 index 5daadd9..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-acknowledgements.plist +++ /dev/null @@ -1,60 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - MIT License - -Copyright (c) [2023] [PostHog] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Title - PostHog - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-dummy.m b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-dummy.m deleted file mode 100644 index 50e395c..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_Cable_CableUITests : NSObject -@end -@implementation PodsDummy_Pods_Cable_CableUITests -@end diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Debug-input-files.xcfilelist b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Debug-input-files.xcfilelist deleted file mode 100644 index f4a8c94..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Debug-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks.sh -${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Debug-output-files.xcfilelist b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Debug-output-files.xcfilelist deleted file mode 100644 index d4451ab..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Debug-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Release-input-files.xcfilelist b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Release-input-files.xcfilelist deleted file mode 100644 index f4a8c94..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Release-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks.sh -${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Release-output-files.xcfilelist b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Release-output-files.xcfilelist deleted file mode 100644 index d4451ab..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks-Release-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks.sh b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks.sh deleted file mode 100755 index 3d77bb1..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-frameworks.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -function on_error { - echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" -} -trap 'on_error $LINENO' ERR - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" -BCSYMBOLMAP_DIR="BCSymbolMaps" - - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink -f "${source}")" - fi - - if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then - # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied - find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do - echo "Installing $f" - install_bcsymbolmap "$f" "$destination" - rm "$f" - done - rmdir "${source}/${BCSYMBOLMAP_DIR}" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - elif [ -L "${binary}" ]; then - echo "Destination binary is symlinked..." - dirname="$(dirname "${binary}")" - binary="${dirname}/$(readlink "${binary}")" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - warn_missing_arch=${2:-true} - if [ -r "$source" ]; then - # Copy the dSYM into the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .dSYM "$source")" - binary_name="$(ls "$source/Contents/Resources/DWARF")" - binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" - - # Strip invalid architectures from the dSYM. - if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then - strip_invalid_archs "$binary" "$warn_missing_arch" - fi - if [[ $STRIP_BINARY_RETVAL == 0 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - mkdir -p "${DWARF_DSYM_FOLDER_PATH}" - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" - fi - fi -} - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - warn_missing_arch=${2:-true} - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - if [[ "$warn_missing_arch" == "true" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - fi - STRIP_BINARY_RETVAL=1 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=0 -} - -# Copies the bcsymbolmap files of a vendored framework -install_bcsymbolmap() { - local bcsymbolmap_path="$1" - local destination="${BUILT_PRODUCTS_DIR}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identity - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework" -fi -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-umbrella.h b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-umbrella.h deleted file mode 100644 index 265363e..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double Pods_Cable_CableUITestsVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_Cable_CableUITestsVersionString[]; - diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.debug.xcconfig b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.debug.xcconfig deleted file mode 100644 index 976867a..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.debug.xcconfig +++ /dev/null @@ -1,16 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog/PostHog.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PostHog" -OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.modulemap b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.modulemap deleted file mode 100644 index 187c319..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_Cable_CableUITests { - umbrella header "Pods-Cable-CableUITests-umbrella.h" - - export * - module * { export * } -} diff --git a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.release.xcconfig b/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.release.xcconfig deleted file mode 100644 index 976867a..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITests/Pods-Cable-CableUITests.release.xcconfig +++ /dev/null @@ -1,16 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog/PostHog.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PostHog" -OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-Info.plist b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-Info.plist deleted file mode 100644 index 19cf209..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - ${PODS_DEVELOPMENT_LANGUAGE} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-acknowledgements.markdown b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-acknowledgements.markdown deleted file mode 100644 index b81fb24..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-acknowledgements.markdown +++ /dev/null @@ -1,28 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## PostHog - -MIT License - -Copyright (c) [2023] [PostHog] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-acknowledgements.plist b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-acknowledgements.plist deleted file mode 100644 index 5daadd9..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-acknowledgements.plist +++ /dev/null @@ -1,60 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - MIT License - -Copyright (c) [2023] [PostHog] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Title - PostHog - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-dummy.m b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-dummy.m deleted file mode 100644 index f400376..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_Cable_CableUITestsScreenshot : NSObject -@end -@implementation PodsDummy_Pods_Cable_CableUITestsScreenshot -@end diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Debug-input-files.xcfilelist b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Debug-input-files.xcfilelist deleted file mode 100644 index dd3a87a..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Debug-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks.sh -${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Debug-output-files.xcfilelist b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Debug-output-files.xcfilelist deleted file mode 100644 index d4451ab..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Debug-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Release-input-files.xcfilelist b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Release-input-files.xcfilelist deleted file mode 100644 index dd3a87a..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Release-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks.sh -${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Release-output-files.xcfilelist b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Release-output-files.xcfilelist deleted file mode 100644 index d4451ab..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks-Release-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks.sh b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks.sh deleted file mode 100755 index 3d77bb1..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-frameworks.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -function on_error { - echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" -} -trap 'on_error $LINENO' ERR - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" -BCSYMBOLMAP_DIR="BCSymbolMaps" - - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink -f "${source}")" - fi - - if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then - # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied - find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do - echo "Installing $f" - install_bcsymbolmap "$f" "$destination" - rm "$f" - done - rmdir "${source}/${BCSYMBOLMAP_DIR}" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - elif [ -L "${binary}" ]; then - echo "Destination binary is symlinked..." - dirname="$(dirname "${binary}")" - binary="${dirname}/$(readlink "${binary}")" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - warn_missing_arch=${2:-true} - if [ -r "$source" ]; then - # Copy the dSYM into the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .dSYM "$source")" - binary_name="$(ls "$source/Contents/Resources/DWARF")" - binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" - - # Strip invalid architectures from the dSYM. - if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then - strip_invalid_archs "$binary" "$warn_missing_arch" - fi - if [[ $STRIP_BINARY_RETVAL == 0 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - mkdir -p "${DWARF_DSYM_FOLDER_PATH}" - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" - fi - fi -} - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - warn_missing_arch=${2:-true} - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - if [[ "$warn_missing_arch" == "true" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - fi - STRIP_BINARY_RETVAL=1 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=0 -} - -# Copies the bcsymbolmap files of a vendored framework -install_bcsymbolmap() { - local bcsymbolmap_path="$1" - local destination="${BUILT_PRODUCTS_DIR}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identity - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework" -fi -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-umbrella.h b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-umbrella.h deleted file mode 100644 index b06e7e5..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double Pods_Cable_CableUITestsScreenshotVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_Cable_CableUITestsScreenshotVersionString[]; - diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.debug.xcconfig b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.debug.xcconfig deleted file mode 100644 index 976867a..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.debug.xcconfig +++ /dev/null @@ -1,16 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog/PostHog.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PostHog" -OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.modulemap b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.modulemap deleted file mode 100644 index 6c55d28..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_Cable_CableUITestsScreenshot { - umbrella header "Pods-Cable-CableUITestsScreenshot-umbrella.h" - - export * - module * { export * } -} diff --git a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.release.xcconfig b/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.release.xcconfig deleted file mode 100644 index 976867a..0000000 --- a/Pods/Target Support Files/Pods-Cable-CableUITestsScreenshot/Pods-Cable-CableUITestsScreenshot.release.xcconfig +++ /dev/null @@ -1,16 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog/PostHog.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PostHog" -OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-Info.plist b/Pods/Target Support Files/Pods-Cable/Pods-Cable-Info.plist deleted file mode 100644 index 19cf209..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - ${PODS_DEVELOPMENT_LANGUAGE} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-acknowledgements.markdown b/Pods/Target Support Files/Pods-Cable/Pods-Cable-acknowledgements.markdown deleted file mode 100644 index b81fb24..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-acknowledgements.markdown +++ /dev/null @@ -1,28 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## PostHog - -MIT License - -Copyright (c) [2023] [PostHog] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-acknowledgements.plist b/Pods/Target Support Files/Pods-Cable/Pods-Cable-acknowledgements.plist deleted file mode 100644 index 5daadd9..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-acknowledgements.plist +++ /dev/null @@ -1,60 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - MIT License - -Copyright (c) [2023] [PostHog] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Title - PostHog - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-dummy.m b/Pods/Target Support Files/Pods-Cable/Pods-Cable-dummy.m deleted file mode 100644 index c10074c..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_Cable : NSObject -@end -@implementation PodsDummy_Pods_Cable -@end diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Debug-input-files.xcfilelist b/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Debug-input-files.xcfilelist deleted file mode 100644 index 74e1f97..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Debug-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-Cable/Pods-Cable-frameworks.sh -${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Debug-output-files.xcfilelist b/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Debug-output-files.xcfilelist deleted file mode 100644 index d4451ab..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Debug-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Release-input-files.xcfilelist b/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Release-input-files.xcfilelist deleted file mode 100644 index 74e1f97..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Release-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-Cable/Pods-Cable-frameworks.sh -${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Release-output-files.xcfilelist b/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Release-output-files.xcfilelist deleted file mode 100644 index d4451ab..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks-Release-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PostHog.framework \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks.sh b/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks.sh deleted file mode 100755 index 3d77bb1..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-frameworks.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -function on_error { - echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" -} -trap 'on_error $LINENO' ERR - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" -BCSYMBOLMAP_DIR="BCSymbolMaps" - - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink -f "${source}")" - fi - - if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then - # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied - find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do - echo "Installing $f" - install_bcsymbolmap "$f" "$destination" - rm "$f" - done - rmdir "${source}/${BCSYMBOLMAP_DIR}" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - elif [ -L "${binary}" ]; then - echo "Destination binary is symlinked..." - dirname="$(dirname "${binary}")" - binary="${dirname}/$(readlink "${binary}")" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - warn_missing_arch=${2:-true} - if [ -r "$source" ]; then - # Copy the dSYM into the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .dSYM "$source")" - binary_name="$(ls "$source/Contents/Resources/DWARF")" - binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" - - # Strip invalid architectures from the dSYM. - if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then - strip_invalid_archs "$binary" "$warn_missing_arch" - fi - if [[ $STRIP_BINARY_RETVAL == 0 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - mkdir -p "${DWARF_DSYM_FOLDER_PATH}" - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" - fi - fi -} - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - warn_missing_arch=${2:-true} - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - if [[ "$warn_missing_arch" == "true" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - fi - STRIP_BINARY_RETVAL=1 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=0 -} - -# Copies the bcsymbolmap files of a vendored framework -install_bcsymbolmap() { - local bcsymbolmap_path="$1" - local destination="${BUILT_PRODUCTS_DIR}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identity - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/PostHog/PostHog.framework" -fi -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable-umbrella.h b/Pods/Target Support Files/Pods-Cable/Pods-Cable-umbrella.h deleted file mode 100644 index ec384fd..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double Pods_CableVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_CableVersionString[]; - diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable.debug.xcconfig b/Pods/Target Support Files/Pods-Cable/Pods-Cable.debug.xcconfig deleted file mode 100644 index 3c85c0d..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable.debug.xcconfig +++ /dev/null @@ -1,16 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog/PostHog.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PostHog" -OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable.modulemap b/Pods/Target Support Files/Pods-Cable/Pods-Cable.modulemap deleted file mode 100644 index 006ee7c..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_Cable { - umbrella header "Pods-Cable-umbrella.h" - - export * - module * { export * } -} diff --git a/Pods/Target Support Files/Pods-Cable/Pods-Cable.release.xcconfig b/Pods/Target Support Files/Pods-Cable/Pods-Cable.release.xcconfig deleted file mode 100644 index 3c85c0d..0000000 --- a/Pods/Target Support Files/Pods-Cable/Pods-Cable.release.xcconfig +++ /dev/null @@ -1,16 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog/PostHog.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PostHog" -OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-Info.plist b/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-Info.plist deleted file mode 100644 index 19cf209..0000000 --- a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - ${PODS_DEVELOPMENT_LANGUAGE} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-acknowledgements.markdown b/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-acknowledgements.markdown deleted file mode 100644 index 102af75..0000000 --- a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-acknowledgements.markdown +++ /dev/null @@ -1,3 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: -Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-acknowledgements.plist b/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-acknowledgements.plist deleted file mode 100644 index 7acbad1..0000000 --- a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-acknowledgements.plist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-dummy.m b/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-dummy.m deleted file mode 100644 index f0988bc..0000000 --- a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_CableTests : NSObject -@end -@implementation PodsDummy_Pods_CableTests -@end diff --git a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-umbrella.h b/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-umbrella.h deleted file mode 100644 index cd71761..0000000 --- a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double Pods_CableTestsVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_CableTestsVersionString[]; - diff --git a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.debug.xcconfig b/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.debug.xcconfig deleted file mode 100644 index 990bb22..0000000 --- a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.debug.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog/PostHog.framework/Headers" -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PostHog" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.modulemap b/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.modulemap deleted file mode 100644 index cc996e3..0000000 --- a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_CableTests { - umbrella header "Pods-CableTests-umbrella.h" - - export * - module * { export * } -} diff --git a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.release.xcconfig b/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.release.xcconfig deleted file mode 100644 index 990bb22..0000000 --- a/Pods/Target Support Files/Pods-CableTests/Pods-CableTests.release.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PostHog/PostHog.framework/Headers" -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PostHog" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/PostHog/PostHog-Info.plist b/Pods/Target Support Files/PostHog/PostHog-Info.plist deleted file mode 100644 index 879a061..0000000 --- a/Pods/Target Support Files/PostHog/PostHog-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - ${PODS_DEVELOPMENT_LANGUAGE} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 3.34.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/Pods/Target Support Files/PostHog/PostHog-dummy.m b/Pods/Target Support Files/PostHog/PostHog-dummy.m deleted file mode 100644 index d35e138..0000000 --- a/Pods/Target Support Files/PostHog/PostHog-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_PostHog : NSObject -@end -@implementation PodsDummy_PostHog -@end diff --git a/Pods/Target Support Files/PostHog/PostHog-prefix.pch b/Pods/Target Support Files/PostHog/PostHog-prefix.pch deleted file mode 100644 index beb2a24..0000000 --- a/Pods/Target Support Files/PostHog/PostHog-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/Pods/Target Support Files/PostHog/PostHog-umbrella.h b/Pods/Target Support Files/PostHog/PostHog-umbrella.h deleted file mode 100644 index 4e7c5bc..0000000 --- a/Pods/Target Support Files/PostHog/PostHog-umbrella.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - -#import "PostHog.h" -#import "ph_backward_references_enc.h" -#import "ph_bit_reader_utils.h" -#import "ph_bit_writer_utils.h" -#import "ph_color_cache_utils.h" -#import "ph_common_dec.h" -#import "ph_common_sse2.h" -#import "ph_common_sse41.h" -#import "ph_cost_enc.h" -#import "ph_cpu.h" -#import "ph_decode.h" -#import "ph_dsp.h" -#import "ph_encode.h" -#import "ph_endian_inl_utils.h" -#import "ph_filters_utils.h" -#import "ph_format_constants.h" -#import "ph_histogram_enc.h" -#import "ph_huffman_encode_utils.h" -#import "ph_huffman_utils.h" -#import "ph_lossless.h" -#import "ph_lossless_common.h" -#import "ph_mux.h" -#import "ph_muxi.h" -#import "ph_mux_types.h" -#import "ph_neon.h" -#import "ph_palette.h" -#import "ph_quant.h" -#import "ph_quant_levels_utils.h" -#import "ph_random_utils.h" -#import "ph_rescaler_utils.h" -#import "ph_sharpyuv.h" -#import "ph_sharpyuv_cpu.h" -#import "ph_sharpyuv_csp.h" -#import "ph_sharpyuv_dsp.h" -#import "ph_sharpyuv_gamma.h" -#import "ph_thread_utils.h" -#import "ph_types.h" -#import "ph_utils.h" -#import "ph_vp8i_dec.h" -#import "ph_vp8i_enc.h" -#import "ph_vp8li_dec.h" -#import "ph_vp8li_enc.h" -#import "ph_vp8_dec.h" -#import "ph_webpi_dec.h" -#import "ph_yuv.h" - -FOUNDATION_EXPORT double PostHogVersionNumber; -FOUNDATION_EXPORT const unsigned char PostHogVersionString[]; - diff --git a/Pods/Target Support Files/PostHog/PostHog.debug.xcconfig b/Pods/Target Support Files/PostHog/PostHog.debug.xcconfig deleted file mode 100644 index 6de2d78..0000000 --- a/Pods/Target Support Files/PostHog/PostHog.debug.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PostHog -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/PostHog -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/PostHog/PostHog.modulemap b/Pods/Target Support Files/PostHog/PostHog.modulemap deleted file mode 100644 index 6657cd0..0000000 --- a/Pods/Target Support Files/PostHog/PostHog.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module PostHog { - umbrella header "PostHog-umbrella.h" - - export * - module * { export * } -} diff --git a/Pods/Target Support Files/PostHog/PostHog.release.xcconfig b/Pods/Target Support Files/PostHog/PostHog.release.xcconfig deleted file mode 100644 index 6de2d78..0000000 --- a/Pods/Target Support Files/PostHog/PostHog.release.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PostHog -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "Foundation" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/PostHog -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/PostHog/ResourceBundle-PostHog-PostHog-Info.plist b/Pods/Target Support Files/PostHog/ResourceBundle-PostHog-PostHog-Info.plist deleted file mode 100644 index bdc424a..0000000 --- a/Pods/Target Support Files/PostHog/ResourceBundle-PostHog-PostHog-Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - ${PODS_DEVELOPMENT_LANGUAGE} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - BNDL - CFBundleShortVersionString - 3.34.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - NSPrincipalClass - - - diff --git a/shooter.sh b/shooter.sh index 33ed023..25b2c57 100755 --- a/shooter.sh +++ b/shooter.sh @@ -15,8 +15,8 @@ is_truthy() { } DEVICE_MATRIX=( +# "iPhone 17 Pro Max|26.0|iphone-17-pro-max" "iPad Pro Screenshot|26.0|ipad-pro-13-inch-m4" - "iPhone 17 Pro Max|26.0|iphone-17-pro-max" ) command -v xcparse >/dev/null 2>&1 || { @@ -83,6 +83,7 @@ for device_entry in "${DEVICE_MATRIX[@]}"; do --batteryState charged --batteryLevel 100 \ --wifiBars 3 + xcrun simctl spawn "$UDID" defaults write com.apple.springboard DoNotDisturb -bool true bundle="results-${DEVICE_SLUG}-${lang}.xcresult" outdir="Shots/Screenshots/${DEVICE_SLUG}/$lang"