diff --git a/.github/workflows/build-and-quality-checks-v2.yml b/.github/workflows/build-and-quality-checks-v2.yml index bb4df1d2..d4f19e91 100644 --- a/.github/workflows/build-and-quality-checks-v2.yml +++ b/.github/workflows/build-and-quality-checks-v2.yml @@ -7,33 +7,33 @@ on: jobs: build: name: Code Quality Checks(v2) - runs-on: macOS-latest + runs-on: macos-latest steps: - name: Checkout source branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install xcpretty run: gem install xcpretty - - name: Select Xcode version - run: sudo xcode-select -s '/Applications/Xcode_13.2.1.app/Contents/Developer' +# - name: Select Xcode version +# run: sudo xcode-select -s '/Applications/Xcode_13.2.1.app/Contents/Developer' - name: Build SDK(iOS) run: | - xcodebuild build -scheme RudderSDK-iOS -workspace Rudder.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 13' | xcpretty + xcodebuild build -scheme Rudder-iOS -workspace Rudder.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 14' | xcpretty - name: Build SDK(watchOS) run: | - xcodebuild build -scheme RudderSDK-iOS -workspace Rudder.xcworkspace -destination 'platform=watchOS Simulator,name=Apple Watch Series 7 - 45mm' | xcpretty + xcodebuild build -scheme Rudder-watchOS -workspace Rudder.xcworkspace -destination 'platform=watchOS Simulator,name=Apple Watch Series 7 (45mm)' | xcpretty - name: Build SDK(tvOS) run: | - xcodebuild build -scheme RudderSDK-iOS -workspace Rudder.xcworkspace -destination 'platform=tvOS Simulator,name=Apple TV' | xcpretty + xcodebuild build -scheme Rudder-tvOS -workspace Rudder.xcworkspace -destination 'platform=tvOS Simulator,name=Apple TV' | xcpretty - - name: Build SDK(macOS) - run: | - xcodebuild build -scheme RudderSDK-iOS -workspace Rudder.xcworkspace -destination 'platform=macOS,variant=Mac Catalyst' | xcpretty +# - name: Build SDK(macOS) +# run: | +# xcodebuild build -scheme Rudder-macOS -workspace Rudder.xcworkspace -destination 'platform=macOS,variant=Mac Catalyst' | xcpretty - name: Install Cocoapods run: gem install cocoapods diff --git a/.github/workflows/check-pr-title-v2.yml b/.github/workflows/check-pr-title-v2.yml index 56e0577a..5e79e878 100644 --- a/.github/workflows/check-pr-title-v2.yml +++ b/.github/workflows/check-pr-title-v2.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Check PR title uses: rudderlabs/github-action-check-pr-title@v1.0.7 diff --git a/.github/workflows/deploy-cocoapods-v2.yml b/.github/workflows/deploy-cocoapods-v2.yml index 5366c7a4..f0af24ea 100644 --- a/.github/workflows/deploy-cocoapods-v2.yml +++ b/.github/workflows/deploy-cocoapods-v2.yml @@ -7,13 +7,13 @@ on: jobs: build: name: Deploy to Cocoapods - runs-on: macOS-latest + runs-on: macos-latest steps: - name: Checkout source branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - name: Select Xcode version - run: sudo xcode-select -s '/Applications/Xcode_13.2.1.app/Contents/Developer' +# - name: Select Xcode version +# run: sudo xcode-select -s '/Applications/Xcode_13.2.1.app/Contents/Developer' - name: Install Cocoapods run: gem install cocoapods diff --git a/.github/workflows/draft-new-beta-release.yml b/.github/workflows/draft-new-beta-release.yml new file mode 100644 index 00000000..8786616a --- /dev/null +++ b/.github/workflows/draft-new-beta-release.yml @@ -0,0 +1,103 @@ +name: Draft new Beta release + +on: + workflow_dispatch: + inputs: + beta_version: + description: "Beta version(Only single digit, example: 1)" + required: true + +jobs: + draft-new-beta-release: + name: Draft a new Beta release + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/heads/fix/') || startsWith(github.ref, 'refs/heads/feat/') + steps: + - name: Checkout source branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set Node 16 + uses: actions/setup-node@v3 + with: + node-version: 16 + + # In order to make a commit, we need to initialize a user. + # You may choose to write something less generic here if you want, it doesn't matter functionality wise. + - name: Initialize mandatory git config + run: | + git config user.name "GitHub actions" + git config user.email noreply@github.com + + # Calculate the next release version based on conventional semantic release + - name: Create release branch + id: create-release + env: + HUSKY: 0 + run: | + source_branch_name=${GITHUB_REF##*/} + release_type=beta-release + git fetch origin master-v2 + git fetch --tags origin + git merge origin/master-v2 + current_version=$(jq -r .version package.json) + + npx standard-version --skip.commit --skip.tag --skip.changelog + new_version="$(jq -r .version package.json).beta.${{ github.event.inputs.beta_version }}" + git reset --hard + + branch_name="${release_type}/${new_version}" + + echo "Source branch for new release is $source_branch_name" + echo "Current version is $current_version" + echo "Release type is $release_type" + echo "New version is $new_version" + echo "New release branch name is $branch_name" + git checkout -b "$branch_name" + git push --set-upstream origin "$branch_name" + + echo "source_branch_name=$source_branch_name" >> $GITHUB_OUTPUT + echo "branch_name=$branch_name" >> $GITHUB_OUTPUT + echo "new_version=$new_version" >> $GITHUB_OUTPUT + echo "CURRENT_VERSION_VALUE=$current_version" >> $GITHUB_ENV + echo "NEW_VERSION_VALUE=$new_version" >> $GITHUB_ENV + + - name: Bump version + id: finish-release + env: + HUSKY: 0 + run: | + echo "Current version: $CURRENT_VERSION_VALUE" + echo "New version: $NEW_VERSION_VALUE" + npx replace $CURRENT_VERSION_VALUE $NEW_VERSION_VALUE package.json + git add package.json + echo ${{ steps.create-release.outputs.new_version }} + echo "commit_summary=$SUMMARY" >> $GITHUB_OUTPUT + git commit -m "chore(beta-relase): $NEW_VERSION_VALUE" + git tag -a "v$NEW_VERSION_VALUE" -m "chore: release v$NEW_VERSION_VALUE" + git push origin "v$NEW_VERSION_VALUE" + + - name: Push new version in release branch & tag + run: | + git push + + - name: Checkout + uses: actions/checkout@v4 + with: + ref: '${{ steps.create-release.outputs.branch_name }}' + + - name: Install Cocoapods + run: gem install cocoapods + + - name: 'Convert podspec to podspec.json' + run: | + pod ipc spec Rudder.podspec > Rudder.podspec.json + + - name: Add Private Spec Repo to CocoaPods installation + run: | + pod repo add rudderlabs https://${{secrets.PAT_USERNAME}}:${{secrets.PAT}}@github.com/rudderlabs/Specs.git + + - name: Add Podspec to repo + run: | + pod repo push rudderlabs Rudder.podspec.json diff --git a/.github/workflows/draft-new-release-v2.yml b/.github/workflows/draft-new-release-v2.yml index e735c7ce..2a26fb00 100644 --- a/.github/workflows/draft-new-release-v2.yml +++ b/.github/workflows/draft-new-release-v2.yml @@ -10,7 +10,7 @@ jobs: if: startsWith(github.ref, 'refs/heads/develop-v2') || startsWith(github.ref, 'refs/heads/hotfix-v2/') steps: - name: Checkout source branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/publish-new-release-v2.yml b/.github/workflows/publish-new-release-v2.yml index 1cada18a..cffe3fc3 100644 --- a/.github/workflows/publish-new-release-v2.yml +++ b/.github/workflows/publish-new-release-v2.yml @@ -22,7 +22,7 @@ jobs: echo "release_version=$VERSION" >> $GITHUB_OUTPUT - name: Checkout source branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/test-v2.yml b/.github/workflows/test-v2.yml index 0f1335d4..44bb24c8 100644 --- a/.github/workflows/test-v2.yml +++ b/.github/workflows/test-v2.yml @@ -10,36 +10,42 @@ on: jobs: build: name: 'Tests & Coverage(v2)' - runs-on: macOS-latest - + runs-on: macos-latest + env: + BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + + - name: Install sonar-scanner and build-wrapper + uses: SonarSource/sonarcloud-github-c-cpp@v2 - name: Install xcpretty run: gem install xcpretty -# - name: Run tests(suite) -# run: | -# xcodebuild -scheme RudderSDK-iOS test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13' | xcpretty - -# - name: Run tests(1) -# run: | -# xcodebuild -scheme RudderSDK-iOS test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13' -only-testing "Tests/Tests" | xcpretty - -# - name: Run tests(2) -# run: | -# xcodebuild -scheme RudderSDK-iOS test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13' -only-testing "Tests/Tests2" | xcpretty - -# - name: Run tests(3) -# run: | -# xcodebuild -scheme RudderSDK-iOS test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13' -only-testing "Tests/Tests3" | xcpretty - - - name: Install SonarCloud - run: npm install -g sonarqube-scanner - + - name: Run tests(iOS) + run: | + xcodebuild -workspace Rudder.xcworkspace -scheme RudderTests-iOS test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -enableCodeCoverage YES -derivedDataPath build | xcpretty + + - name: Run tests(tvOS) + run: | + xcodebuild -workspace Rudder.xcworkspace -scheme RudderTests-tvOS test -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' -enableCodeCoverage YES -derivedDataPath build | xcpretty + + - name: Run tests(watchOS) + run: | + xcodebuild -workspace Rudder.xcworkspace -scheme RudderTests-watchOS test -sdk watchsimulator -destination 'platform=watchOS Simulator,name=Apple Watch Series 7 (45mm)' -enableCodeCoverage YES -derivedDataPath build | xcpretty + + - name: Collect coverage into one XML report + run: | + bash xccov-to-generic.sh build/Logs/Test/*.xcresult/ > generic-coverage.xml + - name: SonarCloud Scan env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sonar-scanner -Dsonar.host.url=https://sonarcloud.io + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 715d357e..8746c6ff 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,12 @@ fastlane/test_output # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ -Configuration.json .DS_Store -.scannerwork \ No newline at end of file +.scannerwork + +# Sonar +relative_or_absolute_path_to_cache_location +compile_commands.json +GoogleService-Info.plist +RudderConfig.plist +configuration.json \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml index fbcddbc6..25debb45 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -14,7 +14,7 @@ excluded: # paths to ignore during linting. Takes precedence over `included`. - Carthage - Pods - Examples - + - RudderTests analyzer_rules: # Rules run by `swiftlint analyze` (experimental) - explicit_self diff --git a/Examples/RudderConfig/RudderConfig.swift b/Examples/RudderConfig/RudderConfig.swift new file mode 100644 index 00000000..4efa2614 --- /dev/null +++ b/Examples/RudderConfig/RudderConfig.swift @@ -0,0 +1,30 @@ +// +// RudderConfig.swift +// RudderOneTrust +// +// Created by Pallab Maiti on 26/01/23. +// + +import Foundation + +@objc +class RudderConfig: NSObject, Codable { + @objc let WRITE_KEY: String + @objc let PROD_DATA_PLANE_URL: String + @objc let PROD_CONTROL_PLANE_URL: String + @objc let LOCAL_DATA_PLANE_URL: String + @objc let LOCAL_CONTROL_PLANE_URL: String + @objc let DEV_DATA_PLANE_URL: String + @objc let DEV_CONTROL_PLANE_URL: String + @objc let STORAGE_LOCATION: String + @objc let DOMAIN_IDENTIFIER: String + + @objc + class func create(from url: URL) -> RudderConfig? { + if let data = try? Data(contentsOf: url), + let rudderConfig = try? PropertyListDecoder().decode(RudderConfig.self, from: data) { + return rudderConfig + } + return nil + } +} diff --git a/Examples/RudderConfig/SampleRudderConfig.plist b/Examples/RudderConfig/SampleRudderConfig.plist new file mode 100644 index 00000000..4e130dde --- /dev/null +++ b/Examples/RudderConfig/SampleRudderConfig.plist @@ -0,0 +1,24 @@ + + + + + WRITE_KEY + + PROD_DATA_PLANE_URL + + PROD_CONTROL_PLANE_URL + + LOCAL_DATA_PLANE_URL + + LOCAL_CONTROL_PLANE_URL + + DEV_DATA_PLANE_URL + + DEV_CONTROL_PLANE_URL + + STORAGE_LOCATION + + DOMAIN_IDENTIFIER + + + diff --git a/Examples/SampleObjC-tvOS/SampleObjC-tvOS/AppDelegate.m b/Examples/SampleObjC-tvOS/SampleObjC-tvOS/AppDelegate.m index 43eb00d4..02f9ffd5 100644 --- a/Examples/SampleObjC-tvOS/SampleObjC-tvOS/AppDelegate.m +++ b/Examples/SampleObjC-tvOS/SampleObjC-tvOS/AppDelegate.m @@ -27,6 +27,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [config dataPlaneURL:DATA_PLANE_URL]; [config trackLifecycleEvents:YES]; [config recordScreenViews:YES]; + [config loglevel:RSLogLevelDebug]; client = [RSClient sharedInstance]; [client configureWith:config]; [client track:@"track 1"]; diff --git a/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App (Complication).xcscheme b/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App (Complication).xcscheme new file mode 100644 index 00000000..de302bc2 --- /dev/null +++ b/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App (Complication).xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App (Notification).xcscheme b/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App (Notification).xcscheme new file mode 100644 index 00000000..e073099b --- /dev/null +++ b/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App (Notification).xcscheme @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App.xcscheme b/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App.xcscheme new file mode 100644 index 00000000..7895bc57 --- /dev/null +++ b/Examples/SampleObjC-watchOS/SampleObjC-watchOS.xcodeproj/xcshareddata/xcschemes/SampleObjC-watchOS WatchKit App.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj/project.pbxproj b/Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj/project.pbxproj index e092456b..df91196f 100644 --- a/Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj/project.pbxproj +++ b/Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -14,7 +14,12 @@ 06EABC8F24665E480043D720 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 06EABC8E24665E480043D720 /* Assets.xcassets */; }; 06EABC9224665E480043D720 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 06EABC9024665E480043D720 /* LaunchScreen.storyboard */; }; 1BBDBFA2CA1F65262723A637 /* Pods_SampleSwift_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72016B33854F923C48E9084A /* Pods_SampleSwift_iOS.framework */; }; + ED333EC62B2358B4003EB0B3 /* RudderConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = ED333EC32B2358B4003EB0B3 /* RudderConfig.plist */; }; + ED333EC72B2358B4003EB0B3 /* SampleRudderConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = ED333EC42B2358B4003EB0B3 /* SampleRudderConfig.plist */; }; + ED333EC82B2358B4003EB0B3 /* RudderConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EC52B2358B4003EB0B3 /* RudderConfig.swift */; }; + ED333ECA2B235D8E003EB0B3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = ED333EC92B235D8E003EB0B3 /* GoogleService-Info.plist */; }; ED4EACA127F5718100207AF1 /* RSCustomDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4EACA027F5718100207AF1 /* RSCustomDestination.swift */; }; + EDC1BBC82B0B2E7D00211F24 /* Configuration.json in Resources */ = {isa = PBXBuildFile; fileRef = EDC1BBC72B0B2E7D00211F24 /* Configuration.json */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -29,7 +34,12 @@ 6AF9E4CE2308F413BD375A80 /* Pods-SampleSwift-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift-iOS.debug.xcconfig"; path = "Target Support Files/Pods-SampleSwift-iOS/Pods-SampleSwift-iOS.debug.xcconfig"; sourceTree = ""; }; 72016B33854F923C48E9084A /* Pods_SampleSwift_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SampleSwift_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 80FF9E334811B6015D51C152 /* Pods-SampleSwift-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift-iOS.release.xcconfig"; path = "Target Support Files/Pods-SampleSwift-iOS/Pods-SampleSwift-iOS.release.xcconfig"; sourceTree = ""; }; + ED333EC32B2358B4003EB0B3 /* RudderConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = RudderConfig.plist; sourceTree = ""; }; + ED333EC42B2358B4003EB0B3 /* SampleRudderConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SampleRudderConfig.plist; sourceTree = ""; }; + ED333EC52B2358B4003EB0B3 /* RudderConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RudderConfig.swift; sourceTree = ""; }; + ED333EC92B235D8E003EB0B3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; ED4EACA027F5718100207AF1 /* RSCustomDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSCustomDestination.swift; sourceTree = ""; }; + EDC1BBC72B0B2E7D00211F24 /* Configuration.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Configuration.json; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,6 +75,8 @@ 06EABC8424665E470043D720 /* SampleSwift-iOS */ = { isa = PBXGroup; children = ( + ED333EC22B2358B4003EB0B3 /* RudderConfig */, + ED333EC92B235D8E003EB0B3 /* GoogleService-Info.plist */, 06EABC8524665E470043D720 /* AppDelegate.swift */, ED4EACA027F5718100207AF1 /* RSCustomDestination.swift */, 06EABC8724665E470043D720 /* SceneDelegate.swift */, @@ -73,6 +85,7 @@ 06EABC8E24665E480043D720 /* Assets.xcassets */, 06EABC9024665E480043D720 /* LaunchScreen.storyboard */, 06EABC9324665E480043D720 /* Info.plist */, + EDC1BBC72B0B2E7D00211F24 /* Configuration.json */, ); path = "SampleSwift-iOS"; sourceTree = ""; @@ -95,6 +108,17 @@ name = Frameworks; sourceTree = ""; }; + ED333EC22B2358B4003EB0B3 /* RudderConfig */ = { + isa = PBXGroup; + children = ( + ED333EC32B2358B4003EB0B3 /* RudderConfig.plist */, + ED333EC52B2358B4003EB0B3 /* RudderConfig.swift */, + ED333EC42B2358B4003EB0B3 /* SampleRudderConfig.plist */, + ); + name = RudderConfig; + path = ../../RudderConfig; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -155,9 +179,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + ED333EC72B2358B4003EB0B3 /* SampleRudderConfig.plist in Resources */, 06EABC9224665E480043D720 /* LaunchScreen.storyboard in Resources */, 06EABC8F24665E480043D720 /* Assets.xcassets in Resources */, + ED333EC62B2358B4003EB0B3 /* RudderConfig.plist in Resources */, + EDC1BBC82B0B2E7D00211F24 /* Configuration.json in Resources */, 06EABC8D24665E470043D720 /* Main.storyboard in Resources */, + ED333ECA2B235D8E003EB0B3 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -211,6 +239,7 @@ buildActionMask = 2147483647; files = ( 06EABC8A24665E470043D720 /* ViewController.swift in Sources */, + ED333EC82B2358B4003EB0B3 /* RudderConfig.swift in Sources */, 06EABC8624665E470043D720 /* AppDelegate.swift in Sources */, 06EABC8824665E470043D720 /* SceneDelegate.swift in Sources */, ED4EACA127F5718100207AF1 /* RSCustomDestination.swift in Sources */, @@ -370,7 +399,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.ios.test.swift; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.swift.ios.v2; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -393,7 +422,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.ios.test.swift; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.swift.ios.v2; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; diff --git a/Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj/xcshareddata/xcschemes/SampleSwift-iOS.xcscheme b/Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj/xcshareddata/xcschemes/SampleSwift-iOS.xcscheme index 3d1f2e51..fe24778c 100644 --- a/Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj/xcshareddata/xcschemes/SampleSwift-iOS.xcscheme +++ b/Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj/xcshareddata/xcschemes/SampleSwift-iOS.xcscheme @@ -50,6 +50,12 @@ ReferencedContainer = "container:SampleSwift-iOS.xcodeproj"> + + + + Bool { - /// Create a `Configuration.json` file on root directory. The JSON should be look like: - /// { - /// "WRITE_KEY": "WRITE_KEY_VALUE", - /// "DATA_PLANE_URL": "DATA_PLANE_URL_VALUE", - /// "CONTROL_PLANE_URL": "CONTROL_PLANE_URL_VALUE" - /// } + guard let path = Bundle.main.path(forResource: "RudderConfig", ofType: "plist"), + let data = try? Data(contentsOf: URL(fileURLWithPath: path)), + let rudderConfig = try? PropertyListDecoder().decode(RudderConfig.self, from: data) else { + return true + } - let filePath = URL(fileURLWithPath: #file).pathComponents.dropLast().dropLast().dropLast().dropLast().joined(separator: "/").replacingOccurrences(of: "//", with: "/") + "/Configuration.json" - do { - let jsonString = try String(contentsOfFile: filePath, encoding: .utf8) - let jsonData = Data(jsonString.utf8) - let configuration = try JSONDecoder().decode(Configuration.self, from: jsonData) - - let config: RSConfig = RSConfig(writeKey: configuration.WRITE_KEY) - .dataPlaneURL(configuration.DATA_PLANE_URL) - .loglevel(.verbose) - .trackLifecycleEvents(false) - .recordScreenViews(true) - .flushQueueSize(8) -// .sleepTimeOut(1) - RSClient.sharedInstance().configure(with: config) + print(NSHomeDirectory()) - /*client?.addDestination(CustomDestination()) + let config: RSConfig = RSConfig(writeKey: rudderConfig.WRITE_KEY) + .dataPlaneURL(rudderConfig.DEV_DATA_PLANE_URL) +// .controlPlaneURL(rudderConfig.DEV_CONTROL_PLANE_URL) + .controlPlaneURL("https://e2e6fd4f-c24c-43d6-8ca3-11a11e7cc7d5.mock.pstmn.io") // disabled +// .controlPlaneURL("https://98e2b8de-9984-471b-a705-b1bcf3f9f6ba.mock.pstmn.io") // enabled + .loglevel(.verbose) + .trackLifecycleEvents(true) + .recordScreenViews(true) + .sleepTimeOut(20) - client?.setAppTrackingConsent(.authorize) - client?.setAnonymousId("example_anonymous_id") - client?.setAdvertisingId(getIDFA()) - client?.setDeviceToken("example_device_token")*/ + RSClient.sharedInstance().configure(with: config) - /*client?.setOptOutStatus(true) - client?.reset() - - let traits = client?.traits - let defaultOption = RSOption() - defaultOption.putIntegration("Amplitude", isEnabled: true) - client?.setOption(defaultOption) - let messageOption = RSOption() - messageOption.putIntegration("MoEngage", isEnabled: true) - messageOption.putExternalId("", withId: "") - client?.identify("Track 2", traits: ["email": "abc@def.com"], option: messageOption)*/ - } catch { } + RSClient.sharedInstance().addDestination(RudderAmplitudeDestination()) + RSClient.sharedInstance().addDestination(RudderFirebaseDestination()) + return true } @@ -79,9 +63,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return ASIdentifierManager.shared().advertisingIdentifier.uuidString } } - -struct Configuration: Codable { - let DATA_PLANE_URL: String - let CONTROL_PLANE_URL: String - let WRITE_KEY: String -} diff --git a/Examples/SampleSwift-iOS/SampleSwift-iOS/ViewController.swift b/Examples/SampleSwift-iOS/SampleSwift-iOS/ViewController.swift index ba57c712..b1f9fe1a 100644 --- a/Examples/SampleSwift-iOS/SampleSwift-iOS/ViewController.swift +++ b/Examples/SampleSwift-iOS/SampleSwift-iOS/ViewController.swift @@ -22,16 +22,42 @@ class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! - let taskList: [Task] = [Task(name: "Identify"), - Task(name: "Single Track"), - Task(name: "Multiple Track"), - Task(name: "Single Flush"), - Task(name: "Multiple Flush From Background"), - Task(name: "Alias"), - Task(name: "Multiple Flush and Multiple Track"), - Task(name: "Screen without properties"), - Task(name: "Screen with properties"), - Task(name: "Reset")] + let taskList: [Task] = [ + Task(name: "Identify with traits"), + Task(name: "Identify without traits"), + Task(name: "Single Track with properties"), + Task(name: "Single Track without properties"), + Task(name: "Alias"), + Task(name: "Screen with properties"), + Task(name: "Screen without properties"), + Task(name: "Group with traits"), + Task(name: "Group without traits"), + Task(name: "Set AnonymousId"), + Task(name: "Set Device Token"), + Task(name: "Set AdvertisingId"), + Task(name: "Opt In"), + Task(name: "Opt Out"), + Task(name: "Reset"), + Task(name: "Single Flush"), + Task(name: "Update AnonymousId"), + Task(name: "Update Device Token"), + Task(name: "Update AdvertisingId"), + Task(name: "Set Option at root level"), + Task(name: "Set Option at identify"), + Task(name: "Set Option at track"), + Task(name: "Start session"), + Task(name: "End session"), + Task(name: "Start session with id"), + Task(name: "Get Context"), + Task(name: "Allowlist track"), + Task(name: "Denylist track"), + Task(name: "Order done"), + Task(name: "Multiple Track"), + Task(name: "Multiple Flush From Background"), + Task(name: "Multiple Flush and Multiple Track"), + Task(name: "Multiple Track, Screen, Alias, Group, Identify"), + Task(name: "Multiple Track, Screen, Alias, Group, Identify, Device Token, AnonymousId, AdvertisingId, AppTracking Consent") + ] override func viewDidLoad() { super.viewDidLoad() @@ -56,18 +82,21 @@ extension ViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch indexPath.row { - case 0: - RSClient.sharedInstance().identify("test_user_id", traits: [ - "integerValue": 42, - "stringValue": "Hello, World!", - "boolValue": true, - "doubleValue": 3.14159, - "arrayValue": [1, 2, 3, 4, 5], - "dictionaryValue": ["key1": "value1", "key2": "value2"], - "urlValue": URL(string: "https://www.example.com")!, - "dateValue": Date() - ]) - case 1: + case 0: + RSClient.sharedInstance().identify("test_user_id", traits: [ + "integerValue": 42, + "stringValue": "Hello, World!", + "boolValue": true, + "doubleValue": 3.14159, + "arrayValue": [1, 2, 3, 4, 5], + "dictionaryValue": ["key1": "value1", "key2": "value2"], + "urlValue": URL(string: "https://www.example.com")!, + "dateValue": Date() + ]) + case 1: + RSClient.sharedInstance().identify("test_user_id") + + case 2: RSClient.sharedInstance().track("single_track_call", properties: [ "integerValue": 42, "stringValue": "Hello, World!", @@ -78,12 +107,115 @@ extension ViewController: UITableViewDataSource, UITableViewDelegate { "urlValue": URL(string: "https://www.example.com")!, "dateValue": Date() ]) - case 2: + case 3: + RSClient.sharedInstance().track("single_track_call") + case 4: + RSClient.sharedInstance().alias("new_user_id") + case 5: + RSClient.sharedInstance().screen("ViewController", properties: ["key_1": "value_1"]) + case 6: + RSClient.sharedInstance().screen("ViewController") + case 7: + RSClient.sharedInstance().group("test_group_id", traits: ["key_1": "value_1"]) + case 8: + RSClient.sharedInstance().group("test_group_id") + case 9: + RSClient.sharedInstance().setAnonymousId("anonymous_id_1") + case 10: + RSClient.sharedInstance().setDeviceToken("device_token_1") + case 11: + RSClient.sharedInstance().setAdvertisingId("advertising_id_1") + case 12: + RSClient.sharedInstance().setOptOutStatus(false) + case 13: + RSClient.sharedInstance().setOptOutStatus(true) + case 14: + RSClient.sharedInstance().reset() + case 15: + RSClient.sharedInstance().flush() + case 16: + RSClient.sharedInstance().setAnonymousId("anonymous_id_2") + case 17: + RSClient.sharedInstance().setDeviceToken("device_token_2") + case 18: + RSClient.sharedInstance().setAdvertisingId("advertising_id_2") + case 19: + let option = RSOption() + + option.putIntegration("key-5", isEnabled: true) + option.putIntegration("key-6", isEnabled: true) + option.putIntegration("key-7", isEnabled: true) + option.putIntegration("key-8", isEnabled: false) + + RSClient.sharedInstance().setOption(option) + case 20: + let option = RSOption() + option.putExternalId("key-1", withId: "value-1") + option.putExternalId("key-2", withId: "value-2") + + option.putIntegration("key-5", isEnabled: true) + option.putIntegration("key-6", isEnabled: true) + option.putIntegration("key-7", isEnabled: false) + option.putIntegration("key-8", isEnabled: false) + + option.putCustomContext(["Key-01": "value-1"], withKey: "key-9") + option.putCustomContext(["Key-02": "value-1"], withKey: "key-10") + option.putCustomContext(["Key-03": "value-1"], withKey: "key-11") + option.putCustomContext(["Key-04": "value-1"], withKey: "key-12") + RSClient.sharedInstance().identify("test_user_id", option: option) + case 21: + let option = RSOption() + option.putExternalId("key-3", withId: "value-3") + option.putExternalId("key-4", withId: "value-4") + + option.putIntegration("key-5", isEnabled: false) + option.putIntegration("key-6", isEnabled: true) + option.putIntegration("key-7", isEnabled: false) + option.putIntegration("key-8", isEnabled: true) + + option.putCustomContext(["Key-01": "value-1"], withKey: "key-9") + option.putCustomContext(["Key-02": "value-2"], withKey: "key-10") + RSClient.sharedInstance().track("single_track_call", option: option) + case 22: + RSClient.sharedInstance().startSession() + case 23: + RSClient.sharedInstance().endSession() + case 24: + RSClient.sharedInstance().startSession(1234567890) + case 25: + if let context = RSClient.sharedInstance().context { + print(context.dictionaryValue) + } + case 26: + RSClient.sharedInstance().track("allow_list_track", properties: [ + "integerValue": 42, + "stringValue": "Hello, World!", + "boolValue": true, + "doubleValue": 3.14159, + "arrayValue": [1, 2, 3, 4, 5], + "dictionaryValue": ["key1": "value1", "key2": Date(), "key-3": ["key1": URL(string: "https://www.example.com")!, "key2": Date()] as [String : Any]] as [String : Any], + "urlValue": URL(string: "https://www.example.com")!, + "dateValue": Date() + ]) + case 27: + RSClient.sharedInstance().track("deny_list_track", properties: [ + "integerValue": 42, + "stringValue": "Hello, World!", + "boolValue": true, + "doubleValue": 3.14159, + "arrayValue": [1, 2, 3, 4, 5], + "dictionaryValue": ["key1": "value1", "key2": Date(), "key-3": ["key1": URL(string: "https://www.example.com")!, "key2": Date()] as [String : Any]] as [String : Any], + "urlValue": URL(string: "https://www.example.com")!, + "dateValue": Date() + ]) + case 28: + RSClient.sharedInstance().track("Order Done", properties: getProperties()) + case 29: + RSClient.sharedInstance().track("Order Completed", properties: getProperties()) + /*case 2: for i in 1...50 { RSClient.sharedInstance().track("Track \(i)", properties: ["time": Date().timeIntervalSince1970]) } - case 3: - RSClient.sharedInstance().flush() case 4: DispatchQueue.global(qos: .background).async { for i in 1...1000 { @@ -104,10 +236,8 @@ extension ViewController: UITableViewDataSource, UITableViewDelegate { print("From Thread 3, Flush No. \(i)") RSClient.sharedInstance().flush() } - } - case 5: - RSClient.sharedInstance().alias("new_user_id") - case 6: + }*/ + /*case 6: DispatchQueue.global(qos: .background).async { for i in 1...1000 { print("From Thread 1A, Track No. \(i)") @@ -149,14 +279,127 @@ extension ViewController: UITableViewDataSource, UITableViewDelegate { RSClient.sharedInstance().flush() } } - case 7: - RSClient.sharedInstance().screen("ViewController") - case 8: - RSClient.sharedInstance().screen("ViewController", properties: ["key_1": "value_1"]) case 9: - RSClient.sharedInstance().reset() + DispatchQueue.global(qos: .background).async { + for i in 1...1000 { + print("From Thread 1A, Track No. \(i)") + RSClient.sharedInstance().track("Track \(i)", properties: ["time": Date().timeIntervalSince1970]) + } + } + + DispatchQueue.global(qos: .background).async { + for i in 1001...2000 { + print("From Thread 2A, Screen No. \(i)") + RSClient.sharedInstance().screen("Screen \(i)", properties: ["time": Date().timeIntervalSince1970]) + } + } + + DispatchQueue.global(qos: .background).async { + for i in 2001...3000 { + print("From Thread 3A, Group No. \(i)") + RSClient.sharedInstance().group("Group \(i)", traits: ["time": "\(Date().timeIntervalSince1970)"]) + } + } + + DispatchQueue.global(qos: .background).async { + for i in 3001...4000 { + print("From Thread 4A, Alias No. \(i)") + RSClient.sharedInstance().alias("Alias \(i)") + } + } + + DispatchQueue.global(qos: .background).async { + for i in 4001...5000 { + print("From Thread 5A, Identify No. \(i)") + RSClient.sharedInstance().identify("Identify \(i)", traits: ["time": Date().timeIntervalSince1970]) + } + } + case 10: + DispatchQueue.global(qos: .background).async { + for i in 1...1000 { + print("From Thread 1A, Track No. \(i)") + if i % 2 == 0 { + RSClient.sharedInstance().track("Track \(i)", properties: ["time": Date().timeIntervalSince1970]) + } else { + RSClient.sharedInstance().setAdvertisingId("Advertising Id \(i)") + } + } + } + + DispatchQueue.global(qos: .background).async { + for i in 1001...2000 { + print("From Thread 2A, Screen No. \(i)") + if i % 2 == 0 { + RSClient.sharedInstance().screen("Screen \(i)", properties: ["time": Date().timeIntervalSince1970]) + } else { + RSClient.sharedInstance().setAnonymousId("Anonymous Id \(i)") + } + } + } + + DispatchQueue.global(qos: .background).async { + for i in 2001...3000 { + print("From Thread 3A, Group No. \(i)") + if i % 2 == 0 { + RSClient.sharedInstance().group("Group \(i)", traits: ["time": "\(Date().timeIntervalSince1970)"]) + } else { + RSClient.sharedInstance().setDeviceToken("Device Token \(i)") + } + } + } + + DispatchQueue.global(qos: .background).async { + for i in 3001...4000 { + print("From Thread 4A, Alias No. \(i)") + if i % 2 == 0 { + RSClient.sharedInstance().alias("Alias \(i)") + } else { + RSClient.sharedInstance().setAppTrackingConsent(.authorize) + } + } + } + + DispatchQueue.global(qos: .background).async { + for i in 4001...5000 { + print("From Thread 5A, Identify No. \(i)") + if i % 2 == 0 { + RSClient.sharedInstance().identify("Identify \(i)", traits: ["time": Date().timeIntervalSince1970]) + } else { + RSClient.sharedInstance().setDeviceToken("Device Token \(i)") + RSClient.sharedInstance().setAppTrackingConsent(.authorize) + } + } + } + */ default: break } } + + func getProperties() -> [String: Any] { + let products: [String: Any] = [ + RSKeys.Ecommerce.productId: "1001", + RSKeys.Ecommerce.productName: "Books-1", + RSKeys.Ecommerce.category: "Books", + RSKeys.Ecommerce.sku: "Books-sku", + RSKeys.Ecommerce.quantity: 2, + RSKeys.Ecommerce.price: 1203.2 + ] + let fullPath = getDocumentsDirectory().appendingPathComponent("randomFilename") + func getDocumentsDirectory() -> URL { + let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + return paths[0] + } + let properties: [String: Any] = [ + RSKeys.Ecommerce.products: [products], + "optOutOfSession": true, + RSKeys.Ecommerce.revenue: 1203, + RSKeys.Ecommerce.quantity: 10, + RSKeys.Ecommerce.price: 101.34, + RSKeys.Ecommerce.productId: "123", + "revenue_type": "revenue_type_value", + "receipt": fullPath + ] + return properties + } } diff --git a/Examples/SampleSwift-macOS/SampleSwift-macOS.xcodeproj/project.pbxproj b/Examples/SampleSwift-macOS/SampleSwift-macOS.xcodeproj/project.pbxproj index 036a77c1..7da87de6 100644 --- a/Examples/SampleSwift-macOS/SampleSwift-macOS.xcodeproj/project.pbxproj +++ b/Examples/SampleSwift-macOS/SampleSwift-macOS.xcodeproj/project.pbxproj @@ -344,7 +344,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = GTGKNDBD23; + DEVELOPMENT_TEAM = WPX9KRKA8B; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; @@ -375,7 +375,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = GTGKNDBD23; + DEVELOPMENT_TEAM = WPX9KRKA8B; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; diff --git a/Examples/SampleSwift-macOS/SampleSwift-macOS.xcodeproj/xcshareddata/xcschemes/SampleSwift-macOS.xcscheme b/Examples/SampleSwift-macOS/SampleSwift-macOS.xcodeproj/xcshareddata/xcschemes/SampleSwift-macOS.xcscheme new file mode 100644 index 00000000..bdaf0c14 --- /dev/null +++ b/Examples/SampleSwift-macOS/SampleSwift-macOS.xcodeproj/xcshareddata/xcschemes/SampleSwift-macOS.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Podfile b/Podfile index 884783b2..69cedde3 100644 --- a/Podfile +++ b/Podfile @@ -17,6 +17,8 @@ end target 'SampleSwift-iOS' do project 'Examples/SampleSwift-iOS/SampleSwift-iOS.xcodeproj' platform :ios, '13.0' + pod 'RudderFirebase', '1.1.0' + pod 'RudderAmplitude', '1.1.0' shared_pods end diff --git a/Podfile.lock b/Podfile.lock index dba67f38..8c4981ea 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,16 +1,142 @@ PODS: - - Rudder (2.3.0) + - Amplitude (8.8.0): + - AnalyticsConnector (~> 1.0.0) + - AnalyticsConnector (1.0.3) + - FirebaseAnalytics (9.2.0): + - FirebaseAnalytics/AdIdSupport (= 9.2.0) + - FirebaseCore (~> 9.0) + - FirebaseInstallations (~> 9.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseAnalytics/AdIdSupport (9.2.0): + - FirebaseCore (~> 9.0) + - FirebaseInstallations (~> 9.0) + - GoogleAppMeasurement (= 9.2.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseCore (9.6.0): + - FirebaseCoreDiagnostics (~> 9.0) + - FirebaseCoreInternal (~> 9.0) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) + - FirebaseCoreDiagnostics (9.6.0): + - GoogleDataTransport (< 10.0.0, >= 9.1.4) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseCoreInternal (9.6.0): + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - FirebaseInstallations (9.6.0): + - FirebaseCore (~> 9.0) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/UserDefaults (~> 7.7) + - PromisesObjC (~> 2.1) + - GoogleAppMeasurement (9.2.0): + - GoogleAppMeasurement/AdIdSupport (= 9.2.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (9.2.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 9.2.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (9.2.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleDataTransport (9.3.0): + - GoogleUtilities/Environment (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/AppDelegateSwizzler (7.12.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.12.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.12.0): + - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (7.12.0): + - GoogleUtilities/Logger + - GoogleUtilities/Network (7.12.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.12.0)" + - GoogleUtilities/Reachability (7.12.0): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.12.0): + - GoogleUtilities/Logger + - nanopb (2.30909.1): + - nanopb/decode (= 2.30909.1) + - nanopb/encode (= 2.30909.1) + - nanopb/decode (2.30909.1) + - nanopb/encode (2.30909.1) + - PromisesObjC (2.3.1) + - Rudder (2.4.2) + - RudderAmplitude (1.1.0): + - Amplitude (= 8.8.0) + - Rudder (~> 2.0) + - RudderFirebase (1.1.0): + - FirebaseAnalytics (= 9.2.0) + - Rudder (~> 2.0) DEPENDENCIES: - Rudder (from `.`) + - RudderAmplitude (= 1.1.0) + - RudderFirebase (= 1.1.0) + +SPEC REPOS: + https://github.com/CocoaPods/Specs.git: + - Amplitude + - AnalyticsConnector + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseCoreInternal + - FirebaseInstallations + - GoogleAppMeasurement + - GoogleDataTransport + - GoogleUtilities + - nanopb + - PromisesObjC + - RudderAmplitude + - RudderFirebase EXTERNAL SOURCES: Rudder: :path: "." SPEC CHECKSUMS: - Rudder: 368691bcf2588f00cc99004cbc2026675b9cb16f + Amplitude: 710116f3539c225e86fb70a8abdcd20015683132 + AnalyticsConnector: a53214d38ae22734c6266106c0492b37832633a9 + FirebaseAnalytics: af5a03a8dff7648c7b8486f6a78b1368e0268dd3 + FirebaseCore: 2082fffcd855f95f883c0a1641133eb9bbe76d40 + FirebaseCoreDiagnostics: 99a495094b10a57eeb3ae8efa1665700ad0bdaa6 + FirebaseCoreInternal: bca76517fe1ed381e989f5e7d8abb0da8d85bed3 + FirebaseInstallations: 0a115432c4e223c5ab20b0dbbe4cbefa793a0e8e + GoogleAppMeasurement: 7a33224321f975d58c166657260526775d9c6b1a + GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe + GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 + nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 + PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 + Rudder: d40c784d899ea76dab14808054120383d95379ae + RudderAmplitude: 5e7cdf892e635d2814258e217632ce18c196a79c + RudderFirebase: 37388a4c3c82a9938aef238eaaedb50bb183094d -PODFILE CHECKSUM: ee379229e6a0b60bc98226700ace2de002171547 +PODFILE CHECKSUM: 96bec7fcdef20abbb7e05c8f937ef34333f65557 -COCOAPODS: 1.11.3 +COCOAPODS: 1.14.2 diff --git a/Rudder.xcodeproj/project.pbxproj b/Rudder.xcodeproj/project.pbxproj index ad9ca08a..a379b078 100644 --- a/Rudder.xcodeproj/project.pbxproj +++ b/Rudder.xcodeproj/project.pbxproj @@ -10,23 +10,22 @@ 8C6EA4432A0BDADF0049569A /* RSUserSessionPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6EA4422A0BDADF0049569A /* RSUserSessionPlugin.swift */; }; ED05631F291BEB0400BAEE65 /* RSVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED05631E291BEB0400BAEE65 /* RSVersion.swift */; }; ED157C3727CE0EEA00F22202 /* RSmacOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C3627CE0EEA00F22202 /* RSmacOSLifecycleEvents.swift */; }; - ED157C3927CE1C0800F22202 /* RSUserIdPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C3827CE1C0800F22202 /* RSUserIdPlugin.swift */; }; - ED157C4127CFC33800F22202 /* RSAdvertisementIdPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4027CFC33800F22202 /* RSAdvertisementIdPlugin.swift */; }; - ED157C4327CFC6F700F22202 /* RSAnonymousIdPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4227CFC6F700F22202 /* RSAnonymousIdPlugin.swift */; }; ED157C4527CFD99700F22202 /* RSIntegrationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4427CFD99700F22202 /* RSIntegrationPlugin.swift */; }; - ED157C4727CFDEC000F22202 /* RSOptionPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4627CFDEC000F22202 /* RSOptionPlugin.swift */; }; - ED157C4927CFE6CE00F22202 /* RSAppTrackingConsentPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4827CFE6CE00F22202 /* RSAppTrackingConsentPlugin.swift */; }; ED157C4B27CFE86100F22202 /* RSAppTrackingConsent.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4A27CFE86100F22202 /* RSAppTrackingConsent.swift */; }; ED157C4D27D0A63A00F22202 /* RSiOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4C27D0A63A00F22202 /* RSiOSScreenViewEvents.swift */; }; ED157C4F27D0AC0E00F22202 /* RSwatchOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4E27D0AC0E00F22202 /* RSwatchOSScreenViewEvents.swift */; }; - ED157C5427D0CA6900F22202 /* RSGDPRPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5327D0CA6900F22202 /* RSGDPRPlugin.swift */; }; ED157C5627D0FCDC00F22202 /* RSmacOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5527D0FCDC00F22202 /* RSmacOSScreenViewEvents.swift */; }; ED157C5827D0FFA400F22202 /* RSLifeCycle.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5727D0FFA400F22202 /* RSLifeCycle.swift */; }; ED157C5A27D1010D00F22202 /* RSPushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5927D1010D00F22202 /* RSPushNotifications.swift */; }; ED277FA127421B06002A8FEA /* RSEventsAndKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED277FA027421B06002A8FEA /* RSEventsAndKeys.swift */; }; - ED277FF92744F199002A8FEA /* RSAnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED277FF82744F199002A8FEA /* RSAnyCodable.swift */; }; - ED277FFB2744F265002A8FEA /* RSAnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED277FFA2744F265002A8FEA /* RSAnyDecodable.swift */; }; - ED277FFD2744F2AD002A8FEA /* RSAnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED277FFC2744F2AD002A8FEA /* RSAnyEncodable.swift */; }; + ED333EB72B21F324003EB0B3 /* RSSessionStoragePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EB62B21F324003EB0B3 /* RSSessionStoragePlugin.swift */; }; + ED333EB82B21F324003EB0B3 /* RSSessionStoragePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EB62B21F324003EB0B3 /* RSSessionStoragePlugin.swift */; }; + ED333EB92B21F324003EB0B3 /* RSSessionStoragePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EB62B21F324003EB0B3 /* RSSessionStoragePlugin.swift */; }; + ED333EBA2B21F324003EB0B3 /* RSSessionStoragePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EB62B21F324003EB0B3 /* RSSessionStoragePlugin.swift */; }; + ED333EBC2B22055D003EB0B3 /* RSContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EBB2B22055D003EB0B3 /* RSContext.swift */; }; + ED333EBD2B22055D003EB0B3 /* RSContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EBB2B22055D003EB0B3 /* RSContext.swift */; }; + ED333EBE2B22055D003EB0B3 /* RSContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EBB2B22055D003EB0B3 /* RSContext.swift */; }; + ED333EBF2B22055D003EB0B3 /* RSContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED333EBB2B22055D003EB0B3 /* RSContext.swift */; }; ED3D225B279EAC2800EC8366 /* Rudder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06CABB842630C3CA0097BEFF /* Rudder.framework */; }; ED3D229427A3F0C100EC8366 /* RSClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226227A3F0C100EC8366 /* RSClient.swift */; }; ED3D229527A3F0C100EC8366 /* Vendor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226627A3F0C100EC8366 /* Vendor.swift */; }; @@ -41,14 +40,25 @@ ED3D22A427A3F0C100EC8366 /* RSReplayQueuePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227A27A3F0C100EC8366 /* RSReplayQueuePlugin.swift */; }; ED3D22A527A3F0C100EC8366 /* RudderDestinationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227B27A3F0C100EC8366 /* RudderDestinationPlugin.swift */; }; ED3D22A627A3F0C100EC8366 /* RSContextPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227C27A3F0C100EC8366 /* RSContextPlugin.swift */; }; - ED3D22A727A3F0C100EC8366 /* RSDeviceTokenPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227D27A3F0C100EC8366 /* RSDeviceTokenPlugin.swift */; }; ED3D22A827A3F0C100EC8366 /* RSController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227E27A3F0C100EC8366 /* RSController.swift */; }; ED3D22AB27A3F0C100EC8366 /* RSPlugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D228127A3F0C100EC8366 /* RSPlugins.swift */; }; ED3D22AC27A3F0C100EC8366 /* RSClient+Plugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D228227A3F0C100EC8366 /* RSClient+Plugins.swift */; }; ED3D22BA27A3F0C100EC8366 /* RSMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D229227A3F0C100EC8366 /* RSMessage.swift */; }; ED3D22BE27A45B6500EC8366 /* RSTypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D22BD27A45B6500EC8366 /* RSTypeAlias.swift */; }; - ED4EACA927F5EA1F00207AF1 /* RSIdentifyTraitsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4EACA827F5EA1F00207AF1 /* RSIdentifyTraitsPlugin.swift */; }; - ED4EACAB27F5F03C00207AF1 /* RSAliasIdPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4EACAA27F5F03C00207AF1 /* RSAliasIdPlugin.swift */; }; + ED53723C2B15C32000D794EB /* Rudder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDC1BC972B0DD89400211F24 /* Rudder.framework */; }; + ED53723D2B15C32900D794EB /* Rudder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDC1BC522B0DD88A00211F24 /* Rudder.framework */; }; + ED53723E2B15C33300D794EB /* Rudder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDC1BC0D2B0DD87D00211F24 /* Rudder.framework */; }; + ED5372462B1613CE00D794EB /* LogMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5372452B1613CE00D794EB /* LogMessages.swift */; }; + ED5372472B1613CE00D794EB /* LogMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5372452B1613CE00D794EB /* LogMessages.swift */; }; + ED5372482B1613CE00D794EB /* LogMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5372452B1613CE00D794EB /* LogMessages.swift */; }; + ED5372492B1613CE00D794EB /* LogMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5372452B1613CE00D794EB /* LogMessages.swift */; }; + ED53724B2B18ED8E00D794EB /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED53724A2B18ED8E00D794EB /* MockURLProtocol.swift */; }; + ED53724C2B18ED8E00D794EB /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED53724A2B18ED8E00D794EB /* MockURLProtocol.swift */; }; + ED53724D2B18ED8E00D794EB /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED53724A2B18ED8E00D794EB /* MockURLProtocol.swift */; }; + ED53724E2B18ED8E00D794EB /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED53724A2B18ED8E00D794EB /* MockURLProtocol.swift */; }; + ED5603852881C691004B5BEC /* RSUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5603842881C691004B5BEC /* RSUserInfo.swift */; }; + ED5603872882CE6D004B5BEC /* RSSessionStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5603862882CE6D004B5BEC /* RSSessionStorage.swift */; }; + ED560389288316C2004B5BEC /* Data+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED560388288316C2004B5BEC /* Data+Ext.swift */; }; EDA7EF752739119600E73142 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF332739119600E73142 /* API.swift */; }; EDA7EF762739119600E73142 /* RSServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF342739119600E73142 /* RSServiceManager.swift */; }; EDA7EF812739119600E73142 /* RSDatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF402739119600E73142 /* RSDatabaseManager.swift */; }; @@ -56,12 +66,10 @@ EDA7EF852739119600E73142 /* RSConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF462739119600E73142 /* RSConstants.swift */; }; EDA7EF862739119600E73142 /* String+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF482739119600E73142 /* String+Ext.swift */; }; EDA7EF872739119600E73142 /* NSError+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF492739119600E73142 /* NSError+Ext.swift */; }; - EDA7EF882739119600E73142 /* UserDefaults+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF4A2739119600E73142 /* UserDefaults+Ext.swift */; }; EDA7EF8A2739119600E73142 /* RSUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF4D2739119600E73142 /* RSUtils.swift */; }; EDA7EF8B2739119600E73142 /* Rudder.h in Headers */ = {isa = PBXBuildFile; fileRef = EDA7EF4F2739119600E73142 /* Rudder.h */; settings = {ATTRIBUTES = (Public, ); }; }; EDA7EF8D2739119600E73142 /* RSMessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF532739119600E73142 /* RSMessageType.swift */; }; EDA7EF8F2739119600E73142 /* RSErrorCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF552739119600E73142 /* RSErrorCode.swift */; }; - EDA7EF902739119600E73142 /* RSLogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF562739119600E73142 /* RSLogLevel.swift */; }; EDA7EF912739119600E73142 /* RSDBMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF582739119600E73142 /* RSDBMessage.swift */; }; EDA7EF922739119600E73142 /* RSConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF592739119600E73142 /* RSConfig.swift */; }; EDA7EF962739119600E73142 /* RSOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF5D2739119600E73142 /* RSOption.swift */; }; @@ -72,21 +80,214 @@ EDA7EFA22739119600E73142 /* RSServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF6B2739119600E73142 /* RSServiceType.swift */; }; EDC1327C27D614C400AFD833 /* RSClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1327B27D614C400AFD833 /* RSClientTests.swift */; }; EDC132A227D7D61D00AFD833 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132A127D7D61D00AFD833 /* JSON.swift */; }; - EDC132A427D8D55700AFD833 /* RSTrackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132A327D8D55600AFD833 /* RSTrackTests.swift */; }; - EDC132A627D8D5F300AFD833 /* RSIdentifyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132A527D8D5F300AFD833 /* RSIdentifyTests.swift */; }; - EDC132A827D8D61700AFD833 /* RSScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132A727D8D61700AFD833 /* RSScreenTests.swift */; }; - EDC132AA27D8D63D00AFD833 /* RSAliasTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132A927D8D63D00AFD833 /* RSAliasTests.swift */; }; - EDC132AC27D8D64E00AFD833 /* RSGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AB27D8D64E00AFD833 /* RSGroupTests.swift */; }; EDC132AE27D9104700AFD833 /* RSBlackListedEventsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AD27D9104700AFD833 /* RSBlackListedEventsTest.swift */; }; EDC132B027D9106500AFD833 /* RSWhiteListedEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AF27D9106500AFD833 /* RSWhiteListedEventsTests.swift */; }; EDC14DDE2859C7DB00E2E6FE /* RSRepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC14DDD2859C7DB00E2E6FE /* RSRepeatingTimer.swift */; }; + EDC1BBCB2B0DD87D00211F24 /* Rudder.h in Headers */ = {isa = PBXBuildFile; fileRef = EDA7EF4F2739119600E73142 /* Rudder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EDC1BBCD2B0DD87D00211F24 /* RSUserSessionPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6EA4422A0BDADF0049569A /* RSUserSessionPlugin.swift */; }; + EDC1BBCE2B0DD87D00211F24 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132A127D7D61D00AFD833 /* JSON.swift */; }; + EDC1BBCF2B0DD87D00211F24 /* RSiOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226E27A3F0C100EC8366 /* RSiOSLifecycleEvents.swift */; }; + EDC1BBD02B0DD87D00211F24 /* RSServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF6B2739119600E73142 /* RSServiceType.swift */; }; + EDC1BBD12B0DD87D00211F24 /* NSError+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF492739119600E73142 /* NSError+Ext.swift */; }; + EDC1BBD32B0DD87D00211F24 /* RSDestinationConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF622739119600E73142 /* RSDestinationConfig.swift */; }; + EDC1BBD42B0DD87D00211F24 /* RSMessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF532739119600E73142 /* RSMessageType.swift */; }; + EDC1BBD52B0DD87D00211F24 /* RudderDestinationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227B27A3F0C100EC8366 /* RudderDestinationPlugin.swift */; }; + EDC1BBD62B0DD87D00211F24 /* RSwatchOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226B27A3F0C100EC8366 /* RSwatchOSLifecycleEvents.swift */; }; + EDC1BBD72B0DD87D00211F24 /* RSErrorCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF552739119600E73142 /* RSErrorCode.swift */; }; + EDC1BBD82B0DD87D00211F24 /* RSSessionStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5603862882CE6D004B5BEC /* RSSessionStorage.swift */; }; + EDC1BBD92B0DD87D00211F24 /* RSMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D229227A3F0C100EC8366 /* RSMessage.swift */; }; + EDC1BBDA2B0DD87D00211F24 /* RSwatchOSDelegation.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226A27A3F0C100EC8366 /* RSwatchOSDelegation.swift */; }; + EDC1BBDB2B0DD87D00211F24 /* RSConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF462739119600E73142 /* RSConstants.swift */; }; + EDC1BBDC2B0DD87D00211F24 /* RSAtomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD3AA3527C659C80048E61E /* RSAtomic.swift */; }; + EDC1BBDD2B0DD87D00211F24 /* RSReplayQueuePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227A27A3F0C100EC8366 /* RSReplayQueuePlugin.swift */; }; + EDC1BBDE2B0DD87D00211F24 /* RSOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF5D2739119600E73142 /* RSOption.swift */; }; + EDC1BBDF2B0DD87D00211F24 /* RSEventsAndKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED277FA027421B06002A8FEA /* RSEventsAndKeys.swift */; }; + EDC1BBE02B0DD87D00211F24 /* RSIntegrationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4427CFD99700F22202 /* RSIntegrationPlugin.swift */; }; + EDC1BBE12B0DD87D00211F24 /* RSRepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC14DDD2859C7DB00E2E6FE /* RSRepeatingTimer.swift */; }; + EDC1BBE22B0DD87D00211F24 /* RSmacOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C3627CE0EEA00F22202 /* RSmacOSLifecycleEvents.swift */; }; + EDC1BBE32B0DD87D00211F24 /* RSAppTrackingConsent.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4A27CFE86100F22202 /* RSAppTrackingConsent.swift */; }; + EDC1BBE42B0DD87D00211F24 /* RSConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF592739119600E73142 /* RSConfig.swift */; }; + EDC1BBE52B0DD87D00211F24 /* RSKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD3AA1227C64B430048E61E /* RSKeyPath.swift */; }; + EDC1BBE62B0DD87D00211F24 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF332739119600E73142 /* API.swift */; }; + EDC1BBE72B0DD87D00211F24 /* RSDatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF402739119600E73142 /* RSDatabaseManager.swift */; }; + EDC1BBE82B0DD87D00211F24 /* RSVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED05631E291BEB0400BAEE65 /* RSVersion.swift */; }; + EDC1BBE92B0DD87D00211F24 /* RSServerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF612739119600E73142 /* RSServerConfig.swift */; }; + EDC1BBEA2B0DD87D00211F24 /* RSmacOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227227A3F0C100EC8366 /* RSmacOSLifecycleMonitor.swift */; }; + EDC1BBEB2B0DD87D00211F24 /* RSiOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227027A3F0C100EC8366 /* RSiOSLifecycleMonitor.swift */; }; + EDC1BBEC2B0DD87D00211F24 /* RSUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5603842881C691004B5BEC /* RSUserInfo.swift */; }; + EDC1BBEE2B0DD87D00211F24 /* RSPushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5927D1010D00F22202 /* RSPushNotifications.swift */; }; + EDC1BBEF2B0DD87D00211F24 /* RSController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227E27A3F0C100EC8366 /* RSController.swift */; }; + EDC1BBF12B0DD87D00211F24 /* RSwatchOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4E27D0AC0E00F22202 /* RSwatchOSScreenViewEvents.swift */; }; + EDC1BBF32B0DD87D00211F24 /* RSTypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D22BD27A45B6500EC8366 /* RSTypeAlias.swift */; }; + EDC1BBF42B0DD87D00211F24 /* RSiOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4C27D0A63A00F22202 /* RSiOSScreenViewEvents.swift */; }; + EDC1BBF52B0DD87D00211F24 /* RSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF452739119600E73142 /* RSUserDefaults.swift */; }; + EDC1BBF62B0DD87D00211F24 /* RSmacOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5527D0FCDC00F22202 /* RSmacOSScreenViewEvents.swift */; }; + EDC1BBF72B0DD87D00211F24 /* Vendor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226627A3F0C100EC8366 /* Vendor.swift */; }; + EDC1BBF82B0DD87D00211F24 /* RSLifeCycle.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5727D0FFA400F22202 /* RSLifeCycle.swift */; }; + EDC1BBF92B0DD87D00211F24 /* String+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF482739119600E73142 /* String+Ext.swift */; }; + EDC1BBFA2B0DD87D00211F24 /* RSPlugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D228127A3F0C100EC8366 /* RSPlugins.swift */; }; + EDC1BBFB2B0DD87D00211F24 /* RSDestinationDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF632739119600E73142 /* RSDestinationDefinition.swift */; }; + EDC1BBFC2B0DD87D00211F24 /* RSClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226227A3F0C100EC8366 /* RSClient.swift */; }; + EDC1BBFD2B0DD87D00211F24 /* AppleUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226727A3F0C100EC8366 /* AppleUtils.swift */; }; + EDC1BBFE2B0DD87D00211F24 /* RSClient+Plugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D228227A3F0C100EC8366 /* RSClient+Plugins.swift */; }; + EDC1BBFF2B0DD87D00211F24 /* RSContextPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227C27A3F0C100EC8366 /* RSContextPlugin.swift */; }; + EDC1BC002B0DD87D00211F24 /* Data+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED560388288316C2004B5BEC /* Data+Ext.swift */; }; + EDC1BC012B0DD87D00211F24 /* RSServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF342739119600E73142 /* RSServiceManager.swift */; }; + EDC1BC022B0DD87D00211F24 /* RSDBMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF582739119600E73142 /* RSDBMessage.swift */; }; + EDC1BC032B0DD87D00211F24 /* RSDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF642739119600E73142 /* RSDestination.swift */; }; + EDC1BC042B0DD87D00211F24 /* RSiOSDelegation.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226F27A3F0C100EC8366 /* RSiOSDelegation.swift */; }; + EDC1BC052B0DD87D00211F24 /* RSwatchOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226C27A3F0C100EC8366 /* RSwatchOSLifecycleMonitor.swift */; }; + EDC1BC062B0DD87D00211F24 /* RSUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF4D2739119600E73142 /* RSUtils.swift */; }; + EDC1BC102B0DD88A00211F24 /* Rudder.h in Headers */ = {isa = PBXBuildFile; fileRef = EDA7EF4F2739119600E73142 /* Rudder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EDC1BC122B0DD88A00211F24 /* RSUserSessionPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6EA4422A0BDADF0049569A /* RSUserSessionPlugin.swift */; }; + EDC1BC132B0DD88A00211F24 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132A127D7D61D00AFD833 /* JSON.swift */; }; + EDC1BC142B0DD88A00211F24 /* RSiOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226E27A3F0C100EC8366 /* RSiOSLifecycleEvents.swift */; }; + EDC1BC152B0DD88A00211F24 /* RSServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF6B2739119600E73142 /* RSServiceType.swift */; }; + EDC1BC162B0DD88A00211F24 /* NSError+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF492739119600E73142 /* NSError+Ext.swift */; }; + EDC1BC182B0DD88A00211F24 /* RSDestinationConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF622739119600E73142 /* RSDestinationConfig.swift */; }; + EDC1BC192B0DD88A00211F24 /* RSMessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF532739119600E73142 /* RSMessageType.swift */; }; + EDC1BC1A2B0DD88A00211F24 /* RudderDestinationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227B27A3F0C100EC8366 /* RudderDestinationPlugin.swift */; }; + EDC1BC1B2B0DD88A00211F24 /* RSwatchOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226B27A3F0C100EC8366 /* RSwatchOSLifecycleEvents.swift */; }; + EDC1BC1C2B0DD88A00211F24 /* RSErrorCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF552739119600E73142 /* RSErrorCode.swift */; }; + EDC1BC1D2B0DD88A00211F24 /* RSSessionStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5603862882CE6D004B5BEC /* RSSessionStorage.swift */; }; + EDC1BC1E2B0DD88A00211F24 /* RSMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D229227A3F0C100EC8366 /* RSMessage.swift */; }; + EDC1BC1F2B0DD88A00211F24 /* RSwatchOSDelegation.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226A27A3F0C100EC8366 /* RSwatchOSDelegation.swift */; }; + EDC1BC202B0DD88A00211F24 /* RSConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF462739119600E73142 /* RSConstants.swift */; }; + EDC1BC212B0DD88A00211F24 /* RSAtomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD3AA3527C659C80048E61E /* RSAtomic.swift */; }; + EDC1BC222B0DD88A00211F24 /* RSReplayQueuePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227A27A3F0C100EC8366 /* RSReplayQueuePlugin.swift */; }; + EDC1BC232B0DD88A00211F24 /* RSOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF5D2739119600E73142 /* RSOption.swift */; }; + EDC1BC242B0DD88A00211F24 /* RSEventsAndKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED277FA027421B06002A8FEA /* RSEventsAndKeys.swift */; }; + EDC1BC252B0DD88A00211F24 /* RSIntegrationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4427CFD99700F22202 /* RSIntegrationPlugin.swift */; }; + EDC1BC262B0DD88A00211F24 /* RSRepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC14DDD2859C7DB00E2E6FE /* RSRepeatingTimer.swift */; }; + EDC1BC272B0DD88A00211F24 /* RSmacOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C3627CE0EEA00F22202 /* RSmacOSLifecycleEvents.swift */; }; + EDC1BC282B0DD88A00211F24 /* RSAppTrackingConsent.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4A27CFE86100F22202 /* RSAppTrackingConsent.swift */; }; + EDC1BC292B0DD88A00211F24 /* RSConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF592739119600E73142 /* RSConfig.swift */; }; + EDC1BC2A2B0DD88A00211F24 /* RSKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD3AA1227C64B430048E61E /* RSKeyPath.swift */; }; + EDC1BC2B2B0DD88A00211F24 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF332739119600E73142 /* API.swift */; }; + EDC1BC2C2B0DD88A00211F24 /* RSDatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF402739119600E73142 /* RSDatabaseManager.swift */; }; + EDC1BC2D2B0DD88A00211F24 /* RSVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED05631E291BEB0400BAEE65 /* RSVersion.swift */; }; + EDC1BC2E2B0DD88A00211F24 /* RSServerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF612739119600E73142 /* RSServerConfig.swift */; }; + EDC1BC2F2B0DD88A00211F24 /* RSmacOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227227A3F0C100EC8366 /* RSmacOSLifecycleMonitor.swift */; }; + EDC1BC302B0DD88A00211F24 /* RSiOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227027A3F0C100EC8366 /* RSiOSLifecycleMonitor.swift */; }; + EDC1BC312B0DD88A00211F24 /* RSUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5603842881C691004B5BEC /* RSUserInfo.swift */; }; + EDC1BC332B0DD88A00211F24 /* RSPushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5927D1010D00F22202 /* RSPushNotifications.swift */; }; + EDC1BC342B0DD88A00211F24 /* RSController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227E27A3F0C100EC8366 /* RSController.swift */; }; + EDC1BC362B0DD88A00211F24 /* RSwatchOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4E27D0AC0E00F22202 /* RSwatchOSScreenViewEvents.swift */; }; + EDC1BC382B0DD88A00211F24 /* RSTypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D22BD27A45B6500EC8366 /* RSTypeAlias.swift */; }; + EDC1BC392B0DD88A00211F24 /* RSiOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4C27D0A63A00F22202 /* RSiOSScreenViewEvents.swift */; }; + EDC1BC3A2B0DD88A00211F24 /* RSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF452739119600E73142 /* RSUserDefaults.swift */; }; + EDC1BC3B2B0DD88A00211F24 /* RSmacOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5527D0FCDC00F22202 /* RSmacOSScreenViewEvents.swift */; }; + EDC1BC3C2B0DD88A00211F24 /* Vendor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226627A3F0C100EC8366 /* Vendor.swift */; }; + EDC1BC3D2B0DD88A00211F24 /* RSLifeCycle.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5727D0FFA400F22202 /* RSLifeCycle.swift */; }; + EDC1BC3E2B0DD88A00211F24 /* String+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF482739119600E73142 /* String+Ext.swift */; }; + EDC1BC3F2B0DD88A00211F24 /* RSPlugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D228127A3F0C100EC8366 /* RSPlugins.swift */; }; + EDC1BC402B0DD88A00211F24 /* RSDestinationDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF632739119600E73142 /* RSDestinationDefinition.swift */; }; + EDC1BC412B0DD88A00211F24 /* RSClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226227A3F0C100EC8366 /* RSClient.swift */; }; + EDC1BC422B0DD88A00211F24 /* AppleUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226727A3F0C100EC8366 /* AppleUtils.swift */; }; + EDC1BC432B0DD88A00211F24 /* RSClient+Plugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D228227A3F0C100EC8366 /* RSClient+Plugins.swift */; }; + EDC1BC442B0DD88A00211F24 /* RSContextPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227C27A3F0C100EC8366 /* RSContextPlugin.swift */; }; + EDC1BC452B0DD88A00211F24 /* Data+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED560388288316C2004B5BEC /* Data+Ext.swift */; }; + EDC1BC462B0DD88A00211F24 /* RSServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF342739119600E73142 /* RSServiceManager.swift */; }; + EDC1BC472B0DD88A00211F24 /* RSDBMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF582739119600E73142 /* RSDBMessage.swift */; }; + EDC1BC482B0DD88A00211F24 /* RSDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF642739119600E73142 /* RSDestination.swift */; }; + EDC1BC492B0DD88A00211F24 /* RSiOSDelegation.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226F27A3F0C100EC8366 /* RSiOSDelegation.swift */; }; + EDC1BC4A2B0DD88A00211F24 /* RSwatchOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226C27A3F0C100EC8366 /* RSwatchOSLifecycleMonitor.swift */; }; + EDC1BC4B2B0DD88A00211F24 /* RSUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF4D2739119600E73142 /* RSUtils.swift */; }; + EDC1BC552B0DD89400211F24 /* Rudder.h in Headers */ = {isa = PBXBuildFile; fileRef = EDA7EF4F2739119600E73142 /* Rudder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EDC1BC572B0DD89400211F24 /* RSUserSessionPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6EA4422A0BDADF0049569A /* RSUserSessionPlugin.swift */; }; + EDC1BC582B0DD89400211F24 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132A127D7D61D00AFD833 /* JSON.swift */; }; + EDC1BC592B0DD89400211F24 /* RSiOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226E27A3F0C100EC8366 /* RSiOSLifecycleEvents.swift */; }; + EDC1BC5A2B0DD89400211F24 /* RSServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF6B2739119600E73142 /* RSServiceType.swift */; }; + EDC1BC5B2B0DD89400211F24 /* NSError+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF492739119600E73142 /* NSError+Ext.swift */; }; + EDC1BC5D2B0DD89400211F24 /* RSDestinationConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF622739119600E73142 /* RSDestinationConfig.swift */; }; + EDC1BC5E2B0DD89400211F24 /* RSMessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF532739119600E73142 /* RSMessageType.swift */; }; + EDC1BC5F2B0DD89400211F24 /* RudderDestinationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227B27A3F0C100EC8366 /* RudderDestinationPlugin.swift */; }; + EDC1BC602B0DD89400211F24 /* RSwatchOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226B27A3F0C100EC8366 /* RSwatchOSLifecycleEvents.swift */; }; + EDC1BC612B0DD89400211F24 /* RSErrorCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF552739119600E73142 /* RSErrorCode.swift */; }; + EDC1BC622B0DD89400211F24 /* RSSessionStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5603862882CE6D004B5BEC /* RSSessionStorage.swift */; }; + EDC1BC632B0DD89400211F24 /* RSMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D229227A3F0C100EC8366 /* RSMessage.swift */; }; + EDC1BC642B0DD89400211F24 /* RSwatchOSDelegation.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226A27A3F0C100EC8366 /* RSwatchOSDelegation.swift */; }; + EDC1BC652B0DD89400211F24 /* RSConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF462739119600E73142 /* RSConstants.swift */; }; + EDC1BC662B0DD89400211F24 /* RSAtomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD3AA3527C659C80048E61E /* RSAtomic.swift */; }; + EDC1BC672B0DD89400211F24 /* RSReplayQueuePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227A27A3F0C100EC8366 /* RSReplayQueuePlugin.swift */; }; + EDC1BC682B0DD89400211F24 /* RSOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF5D2739119600E73142 /* RSOption.swift */; }; + EDC1BC692B0DD89400211F24 /* RSEventsAndKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED277FA027421B06002A8FEA /* RSEventsAndKeys.swift */; }; + EDC1BC6A2B0DD89400211F24 /* RSIntegrationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4427CFD99700F22202 /* RSIntegrationPlugin.swift */; }; + EDC1BC6B2B0DD89400211F24 /* RSRepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC14DDD2859C7DB00E2E6FE /* RSRepeatingTimer.swift */; }; + EDC1BC6C2B0DD89400211F24 /* RSmacOSLifecycleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C3627CE0EEA00F22202 /* RSmacOSLifecycleEvents.swift */; }; + EDC1BC6D2B0DD89400211F24 /* RSAppTrackingConsent.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4A27CFE86100F22202 /* RSAppTrackingConsent.swift */; }; + EDC1BC6E2B0DD89400211F24 /* RSConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF592739119600E73142 /* RSConfig.swift */; }; + EDC1BC6F2B0DD89400211F24 /* RSKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD3AA1227C64B430048E61E /* RSKeyPath.swift */; }; + EDC1BC702B0DD89400211F24 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF332739119600E73142 /* API.swift */; }; + EDC1BC712B0DD89400211F24 /* RSDatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF402739119600E73142 /* RSDatabaseManager.swift */; }; + EDC1BC722B0DD89400211F24 /* RSVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED05631E291BEB0400BAEE65 /* RSVersion.swift */; }; + EDC1BC732B0DD89400211F24 /* RSServerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF612739119600E73142 /* RSServerConfig.swift */; }; + EDC1BC742B0DD89400211F24 /* RSmacOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227227A3F0C100EC8366 /* RSmacOSLifecycleMonitor.swift */; }; + EDC1BC752B0DD89400211F24 /* RSiOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227027A3F0C100EC8366 /* RSiOSLifecycleMonitor.swift */; }; + EDC1BC762B0DD89400211F24 /* RSUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5603842881C691004B5BEC /* RSUserInfo.swift */; }; + EDC1BC782B0DD89400211F24 /* RSPushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5927D1010D00F22202 /* RSPushNotifications.swift */; }; + EDC1BC792B0DD89400211F24 /* RSController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227E27A3F0C100EC8366 /* RSController.swift */; }; + EDC1BC7B2B0DD89400211F24 /* RSwatchOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4E27D0AC0E00F22202 /* RSwatchOSScreenViewEvents.swift */; }; + EDC1BC7D2B0DD89400211F24 /* RSTypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D22BD27A45B6500EC8366 /* RSTypeAlias.swift */; }; + EDC1BC7E2B0DD89400211F24 /* RSiOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C4C27D0A63A00F22202 /* RSiOSScreenViewEvents.swift */; }; + EDC1BC7F2B0DD89400211F24 /* RSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF452739119600E73142 /* RSUserDefaults.swift */; }; + EDC1BC802B0DD89400211F24 /* RSmacOSScreenViewEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5527D0FCDC00F22202 /* RSmacOSScreenViewEvents.swift */; }; + EDC1BC812B0DD89400211F24 /* Vendor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226627A3F0C100EC8366 /* Vendor.swift */; }; + EDC1BC822B0DD89400211F24 /* RSLifeCycle.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157C5727D0FFA400F22202 /* RSLifeCycle.swift */; }; + EDC1BC832B0DD89400211F24 /* String+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF482739119600E73142 /* String+Ext.swift */; }; + EDC1BC842B0DD89400211F24 /* RSPlugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D228127A3F0C100EC8366 /* RSPlugins.swift */; }; + EDC1BC852B0DD89400211F24 /* RSDestinationDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF632739119600E73142 /* RSDestinationDefinition.swift */; }; + EDC1BC862B0DD89400211F24 /* RSClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226227A3F0C100EC8366 /* RSClient.swift */; }; + EDC1BC872B0DD89400211F24 /* AppleUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226727A3F0C100EC8366 /* AppleUtils.swift */; }; + EDC1BC882B0DD89400211F24 /* RSClient+Plugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D228227A3F0C100EC8366 /* RSClient+Plugins.swift */; }; + EDC1BC892B0DD89400211F24 /* RSContextPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227C27A3F0C100EC8366 /* RSContextPlugin.swift */; }; + EDC1BC8A2B0DD89400211F24 /* Data+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED560388288316C2004B5BEC /* Data+Ext.swift */; }; + EDC1BC8B2B0DD89400211F24 /* RSServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF342739119600E73142 /* RSServiceManager.swift */; }; + EDC1BC8C2B0DD89400211F24 /* RSDBMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF582739119600E73142 /* RSDBMessage.swift */; }; + EDC1BC8D2B0DD89400211F24 /* RSDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF642739119600E73142 /* RSDestination.swift */; }; + EDC1BC8E2B0DD89400211F24 /* RSiOSDelegation.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226F27A3F0C100EC8366 /* RSiOSDelegation.swift */; }; + EDC1BC8F2B0DD89400211F24 /* RSwatchOSLifecycleMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D226C27A3F0C100EC8366 /* RSwatchOSLifecycleMonitor.swift */; }; + EDC1BC902B0DD89400211F24 /* RSUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA7EF4D2739119600E73142 /* RSUtils.swift */; }; + EDC1BC9D2B1465AC00211F24 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1BC992B10A31C00211F24 /* TestUtils.swift */; }; + EDC1BC9E2B1465AC00211F24 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1BC992B10A31C00211F24 /* TestUtils.swift */; }; + EDC1BC9F2B1465AC00211F24 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1BC992B10A31C00211F24 /* TestUtils.swift */; }; + EDC1BCA02B1465AC00211F24 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1BC992B10A31C00211F24 /* TestUtils.swift */; }; + EDC1BCA52B146A2D00211F24 /* RSClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1327B27D614C400AFD833 /* RSClientTests.swift */; }; + EDC1BCA62B146A2D00211F24 /* RSmacOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF5728489E2C002B2D46 /* RSmacOSLifeCycleEventTests.swift */; }; + EDC1BCA72B146A2D00211F24 /* RSwatchOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF5628489E2C002B2D46 /* RSwatchOSLifeCycleEventTests.swift */; }; + EDC1BCA82B146A2D00211F24 /* RSWhiteListedEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AF27D9106500AFD833 /* RSWhiteListedEventsTests.swift */; }; + EDC1BCA92B146A2D00211F24 /* RSThreadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0C96428225BFB00470E88 /* RSThreadTests.swift */; }; + EDC1BCAA2B146A2D00211F24 /* RSiOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF54284895F9002B2D46 /* RSiOSLifeCycleEventTests.swift */; }; + EDC1BCAB2B146A2D00211F24 /* RSBlackListedEventsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AD27D9104700AFD833 /* RSBlackListedEventsTest.swift */; }; + EDC1BCAC2B146A2D00211F24 /* RSDatabaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0C9662829199200470E88 /* RSDatabaseTests.swift */; }; + EDC1BCB92B146A3800211F24 /* RSClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1327B27D614C400AFD833 /* RSClientTests.swift */; }; + EDC1BCBA2B146A3800211F24 /* RSmacOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF5728489E2C002B2D46 /* RSmacOSLifeCycleEventTests.swift */; }; + EDC1BCBB2B146A3800211F24 /* RSwatchOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF5628489E2C002B2D46 /* RSwatchOSLifeCycleEventTests.swift */; }; + EDC1BCBC2B146A3800211F24 /* RSWhiteListedEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AF27D9106500AFD833 /* RSWhiteListedEventsTests.swift */; }; + EDC1BCBD2B146A3800211F24 /* RSThreadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0C96428225BFB00470E88 /* RSThreadTests.swift */; }; + EDC1BCBE2B146A3800211F24 /* RSiOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF54284895F9002B2D46 /* RSiOSLifeCycleEventTests.swift */; }; + EDC1BCBF2B146A3800211F24 /* RSBlackListedEventsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AD27D9104700AFD833 /* RSBlackListedEventsTest.swift */; }; + EDC1BCC02B146A3800211F24 /* RSDatabaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0C9662829199200470E88 /* RSDatabaseTests.swift */; }; + EDC1BCCD2B146A4E00211F24 /* RSClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1327B27D614C400AFD833 /* RSClientTests.swift */; }; + EDC1BCCE2B146A4E00211F24 /* RSmacOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF5728489E2C002B2D46 /* RSmacOSLifeCycleEventTests.swift */; }; + EDC1BCCF2B146A4E00211F24 /* RSwatchOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF5628489E2C002B2D46 /* RSwatchOSLifeCycleEventTests.swift */; }; + EDC1BCD02B146A4E00211F24 /* RSWhiteListedEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AF27D9106500AFD833 /* RSWhiteListedEventsTests.swift */; }; + EDC1BCD12B146A4E00211F24 /* RSThreadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0C96428225BFB00470E88 /* RSThreadTests.swift */; }; + EDC1BCD22B146A4E00211F24 /* RSiOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF54284895F9002B2D46 /* RSiOSLifeCycleEventTests.swift */; }; + EDC1BCD32B146A4E00211F24 /* RSBlackListedEventsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC132AD27D9104700AFD833 /* RSBlackListedEventsTest.swift */; }; + EDC1BCD42B146A4E00211F24 /* RSDatabaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0C9662829199200470E88 /* RSDatabaseTests.swift */; }; + EDC1BCDE2B14738F00211F24 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1BCDD2B14738F00211F24 /* Logger.swift */; }; + EDC1BCDF2B14738F00211F24 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1BCDD2B14738F00211F24 /* Logger.swift */; }; + EDC1BCE02B14738F00211F24 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1BCDD2B14738F00211F24 /* Logger.swift */; }; + EDC1BCE12B14738F00211F24 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC1BCDD2B14738F00211F24 /* Logger.swift */; }; + EDC1BCE22B15BC4600211F24 /* ServerConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = EDC1BC9B2B10A5DA00211F24 /* ServerConfig.json */; }; + EDC1BCE32B15BC4700211F24 /* ServerConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = EDC1BC9B2B10A5DA00211F24 /* ServerConfig.json */; }; + EDC1BCE42B15BC4800211F24 /* ServerConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = EDC1BC9B2B10A5DA00211F24 /* ServerConfig.json */; }; + EDC1BCE52B15BC4800211F24 /* ServerConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = EDC1BC9B2B10A5DA00211F24 /* ServerConfig.json */; }; EDD0C96528225BFB00470E88 /* RSThreadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0C96428225BFB00470E88 /* RSThreadTests.swift */; }; EDD0C9672829199200470E88 /* RSDatabaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0C9662829199200470E88 /* RSDatabaseTests.swift */; }; EDD3AA1327C64B430048E61E /* RSKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD3AA1227C64B430048E61E /* RSKeyPath.swift */; }; EDD3AA3627C659C80048E61E /* RSAtomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD3AA3527C659C80048E61E /* RSAtomic.swift */; }; - EDD3AA4A27C8DAAA0048E61E /* RSLoggerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227827A3F0C100EC8366 /* RSLoggerPlugin.swift */; }; - EDD3AA4B27C8DAB90048E61E /* RSLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227727A3F0C100EC8366 /* RSLogger.swift */; }; - EDD3AA4C27C8DABD0048E61E /* RSConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3D227927A3F0C100EC8366 /* RSConsoleLogger.swift */; }; EDF3FF55284895F9002B2D46 /* RSiOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF54284895F9002B2D46 /* RSiOSLifeCycleEventTests.swift */; }; EDF3FF5828489E2C002B2D46 /* RSwatchOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF5628489E2C002B2D46 /* RSwatchOSLifeCycleEventTests.swift */; }; EDF3FF5928489E2C002B2D46 /* RSmacOSLifeCycleEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDF3FF5728489E2C002B2D46 /* RSmacOSLifeCycleEventTests.swift */; }; @@ -100,11 +301,32 @@ remoteGlobalIDString = 06CABB832630C3CA0097BEFF; remoteInfo = Rudder; }; + ED53723F2B15C6BC00D794EB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 06CABB7B2630C3CA0097BEFF /* Project object */; + proxyType = 1; + remoteGlobalIDString = EDC1BC0E2B0DD88A00211F24; + remoteInfo = "Rudder-tvOS"; + }; + ED5372412B15C6C600D794EB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 06CABB7B2630C3CA0097BEFF /* Project object */; + proxyType = 1; + remoteGlobalIDString = EDC1BC532B0DD89400211F24; + remoteInfo = "Rudder-watchOS"; + }; + ED5372432B15C6CE00D794EB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 06CABB7B2630C3CA0097BEFF /* Project object */; + proxyType = 1; + remoteGlobalIDString = EDC1BBC92B0DD87D00211F24; + remoteInfo = "Rudder-macOS"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 06CABB842630C3CA0097BEFF /* Rudder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rudder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8C6EA4422A0BDADF0049569A /* RSUserSessionPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RSUserSessionPlugin.swift; path = Sources/Classes/Client/Plugins/RSUserSessionPlugin.swift; sourceTree = ""; }; + 8C6EA4422A0BDADF0049569A /* RSUserSessionPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSUserSessionPlugin.swift; sourceTree = ""; }; ED056315291BB03900BAEE65 /* Configuration.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Configuration.json; sourceTree = ""; }; ED056316291BB03900BAEE65 /* package.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = package.json; sourceTree = ""; }; ED056317291BB03900BAEE65 /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = ""; }; @@ -114,24 +336,17 @@ ED05631D291BE61000BAEE65 /* .github */ = {isa = PBXFileReference; lastKnownFileType = folder; path = .github; sourceTree = ""; }; ED05631E291BEB0400BAEE65 /* RSVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSVersion.swift; sourceTree = ""; }; ED157C3627CE0EEA00F22202 /* RSmacOSLifecycleEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSmacOSLifecycleEvents.swift; sourceTree = ""; }; - ED157C3827CE1C0800F22202 /* RSUserIdPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSUserIdPlugin.swift; sourceTree = ""; }; - ED157C4027CFC33800F22202 /* RSAdvertisementIdPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSAdvertisementIdPlugin.swift; sourceTree = ""; }; - ED157C4227CFC6F700F22202 /* RSAnonymousIdPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSAnonymousIdPlugin.swift; sourceTree = ""; }; ED157C4427CFD99700F22202 /* RSIntegrationPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSIntegrationPlugin.swift; sourceTree = ""; }; - ED157C4627CFDEC000F22202 /* RSOptionPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSOptionPlugin.swift; sourceTree = ""; }; - ED157C4827CFE6CE00F22202 /* RSAppTrackingConsentPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSAppTrackingConsentPlugin.swift; sourceTree = ""; }; ED157C4A27CFE86100F22202 /* RSAppTrackingConsent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSAppTrackingConsent.swift; sourceTree = ""; }; ED157C4C27D0A63A00F22202 /* RSiOSScreenViewEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSiOSScreenViewEvents.swift; sourceTree = ""; }; ED157C4E27D0AC0E00F22202 /* RSwatchOSScreenViewEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSwatchOSScreenViewEvents.swift; sourceTree = ""; }; - ED157C5327D0CA6900F22202 /* RSGDPRPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSGDPRPlugin.swift; sourceTree = ""; }; ED157C5527D0FCDC00F22202 /* RSmacOSScreenViewEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSmacOSScreenViewEvents.swift; sourceTree = ""; }; ED157C5727D0FFA400F22202 /* RSLifeCycle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSLifeCycle.swift; sourceTree = ""; }; ED157C5927D1010D00F22202 /* RSPushNotifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSPushNotifications.swift; sourceTree = ""; }; ED277FA027421B06002A8FEA /* RSEventsAndKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSEventsAndKeys.swift; sourceTree = ""; }; - ED277FF82744F199002A8FEA /* RSAnyCodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSAnyCodable.swift; sourceTree = ""; }; - ED277FFA2744F265002A8FEA /* RSAnyDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSAnyDecodable.swift; sourceTree = ""; }; - ED277FFC2744F2AD002A8FEA /* RSAnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSAnyEncodable.swift; sourceTree = ""; }; - ED3D2257279EAC2800EC8366 /* RudderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RudderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + ED333EB62B21F324003EB0B3 /* RSSessionStoragePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSSessionStoragePlugin.swift; sourceTree = ""; }; + ED333EBB2B22055D003EB0B3 /* RSContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSContext.swift; sourceTree = ""; }; + ED3D2257279EAC2800EC8366 /* RudderTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RudderTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; ED3D226227A3F0C100EC8366 /* RSClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSClient.swift; sourceTree = ""; }; ED3D226627A3F0C100EC8366 /* Vendor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vendor.swift; sourceTree = ""; }; ED3D226727A3F0C100EC8366 /* AppleUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleUtils.swift; sourceTree = ""; }; @@ -142,20 +357,19 @@ ED3D226F27A3F0C100EC8366 /* RSiOSDelegation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSiOSDelegation.swift; sourceTree = ""; }; ED3D227027A3F0C100EC8366 /* RSiOSLifecycleMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSiOSLifecycleMonitor.swift; sourceTree = ""; }; ED3D227227A3F0C100EC8366 /* RSmacOSLifecycleMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSmacOSLifecycleMonitor.swift; sourceTree = ""; }; - ED3D227727A3F0C100EC8366 /* RSLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSLogger.swift; sourceTree = ""; }; - ED3D227827A3F0C100EC8366 /* RSLoggerPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSLoggerPlugin.swift; sourceTree = ""; }; - ED3D227927A3F0C100EC8366 /* RSConsoleLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSConsoleLogger.swift; sourceTree = ""; }; ED3D227A27A3F0C100EC8366 /* RSReplayQueuePlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSReplayQueuePlugin.swift; sourceTree = ""; }; ED3D227B27A3F0C100EC8366 /* RudderDestinationPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RudderDestinationPlugin.swift; sourceTree = ""; }; ED3D227C27A3F0C100EC8366 /* RSContextPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSContextPlugin.swift; sourceTree = ""; }; - ED3D227D27A3F0C100EC8366 /* RSDeviceTokenPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSDeviceTokenPlugin.swift; sourceTree = ""; }; ED3D227E27A3F0C100EC8366 /* RSController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSController.swift; sourceTree = ""; }; ED3D228127A3F0C100EC8366 /* RSPlugins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSPlugins.swift; sourceTree = ""; }; ED3D228227A3F0C100EC8366 /* RSClient+Plugins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RSClient+Plugins.swift"; sourceTree = ""; }; ED3D229227A3F0C100EC8366 /* RSMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSMessage.swift; sourceTree = ""; }; ED3D22BD27A45B6500EC8366 /* RSTypeAlias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSTypeAlias.swift; sourceTree = ""; }; - ED4EACA827F5EA1F00207AF1 /* RSIdentifyTraitsPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSIdentifyTraitsPlugin.swift; sourceTree = ""; }; - ED4EACAA27F5F03C00207AF1 /* RSAliasIdPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSAliasIdPlugin.swift; sourceTree = ""; }; + ED5372452B1613CE00D794EB /* LogMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogMessages.swift; sourceTree = ""; }; + ED53724A2B18ED8E00D794EB /* MockURLProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockURLProtocol.swift; sourceTree = ""; }; + ED5603842881C691004B5BEC /* RSUserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSUserInfo.swift; sourceTree = ""; }; + ED5603862882CE6D004B5BEC /* RSSessionStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSSessionStorage.swift; sourceTree = ""; }; + ED560388288316C2004B5BEC /* Data+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Ext.swift"; sourceTree = ""; }; ED910CDE273BA25100A3EDFF /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; ED910CE0273BA25100A3EDFF /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; ED910CE1273BA25100A3EDFF /* Podfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; @@ -169,13 +383,10 @@ EDA7EF462739119600E73142 /* RSConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSConstants.swift; sourceTree = ""; }; EDA7EF482739119600E73142 /* String+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Ext.swift"; sourceTree = ""; }; EDA7EF492739119600E73142 /* NSError+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSError+Ext.swift"; sourceTree = ""; }; - EDA7EF4A2739119600E73142 /* UserDefaults+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Ext.swift"; sourceTree = ""; }; EDA7EF4D2739119600E73142 /* RSUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSUtils.swift; sourceTree = ""; }; EDA7EF4F2739119600E73142 /* Rudder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Rudder.h; sourceTree = ""; }; - EDA7EF502739119600E73142 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; EDA7EF532739119600E73142 /* RSMessageType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSMessageType.swift; sourceTree = ""; }; EDA7EF552739119600E73142 /* RSErrorCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSErrorCode.swift; sourceTree = ""; }; - EDA7EF562739119600E73142 /* RSLogLevel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSLogLevel.swift; sourceTree = ""; }; EDA7EF582739119600E73142 /* RSDBMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSDBMessage.swift; sourceTree = ""; }; EDA7EF592739119600E73142 /* RSConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSConfig.swift; sourceTree = ""; }; EDA7EF5D2739119600E73142 /* RSOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSOption.swift; sourceTree = ""; }; @@ -186,14 +397,18 @@ EDA7EF6B2739119600E73142 /* RSServiceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSServiceType.swift; sourceTree = ""; }; EDC1327B27D614C400AFD833 /* RSClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSClientTests.swift; sourceTree = ""; }; EDC132A127D7D61D00AFD833 /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = ""; }; - EDC132A327D8D55600AFD833 /* RSTrackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSTrackTests.swift; sourceTree = ""; }; - EDC132A527D8D5F300AFD833 /* RSIdentifyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSIdentifyTests.swift; sourceTree = ""; }; - EDC132A727D8D61700AFD833 /* RSScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSScreenTests.swift; sourceTree = ""; }; - EDC132A927D8D63D00AFD833 /* RSAliasTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSAliasTests.swift; sourceTree = ""; }; - EDC132AB27D8D64E00AFD833 /* RSGroupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSGroupTests.swift; sourceTree = ""; }; EDC132AD27D9104700AFD833 /* RSBlackListedEventsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSBlackListedEventsTest.swift; sourceTree = ""; }; EDC132AF27D9106500AFD833 /* RSWhiteListedEventsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSWhiteListedEventsTests.swift; sourceTree = ""; }; EDC14DDD2859C7DB00E2E6FE /* RSRepeatingTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RSRepeatingTimer.swift; path = Sources/Classes/Helpers/RSRepeatingTimer.swift; sourceTree = SOURCE_ROOT; }; + EDC1BC0D2B0DD87D00211F24 /* Rudder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rudder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EDC1BC522B0DD88A00211F24 /* Rudder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rudder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EDC1BC972B0DD89400211F24 /* Rudder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rudder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EDC1BC992B10A31C00211F24 /* TestUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = ""; }; + EDC1BC9B2B10A5DA00211F24 /* ServerConfig.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ServerConfig.json; sourceTree = ""; }; + EDC1BCB42B146A2D00211F24 /* RudderTests-watchOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RudderTests-watchOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + EDC1BCC82B146A3800211F24 /* RudderTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RudderTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + EDC1BCDC2B146A4E00211F24 /* RudderTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RudderTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + EDC1BCDD2B14738F00211F24 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; EDD0C96428225BFB00470E88 /* RSThreadTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSThreadTests.swift; sourceTree = ""; }; EDD0C9662829199200470E88 /* RSDatabaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSDatabaseTests.swift; sourceTree = ""; }; EDD3AA1227C64B430048E61E /* RSKeyPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSKeyPath.swift; sourceTree = ""; }; @@ -220,16 +435,60 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EDC1BC072B0DD87D00211F24 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BC4C2B0DD88A00211F24 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BC912B0DD89400211F24 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCAD2B146A2D00211F24 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ED53723C2B15C32000D794EB /* Rudder.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCC12B146A3800211F24 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ED53723D2B15C32900D794EB /* Rudder.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCD52B146A4E00211F24 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ED53723E2B15C33300D794EB /* Rudder.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 06CABB7A2630C3CA0097BEFF = { isa = PBXGroup; children = ( - 8C6EA4422A0BDADF0049569A /* RSUserSessionPlugin.swift */, ED056319291BB06C00BAEE65 /* Others */, EDA7EF2D2739119600E73142 /* Sources */, - EDC1325A27D612B600AFD833 /* Tests */, + EDC1325B27D612B600AFD833 /* RudderTests */, 06CABB852630C3CA0097BEFF /* Products */, ); sourceTree = ""; @@ -238,7 +497,13 @@ isa = PBXGroup; children = ( 06CABB842630C3CA0097BEFF /* Rudder.framework */, - ED3D2257279EAC2800EC8366 /* RudderTests.xctest */, + ED3D2257279EAC2800EC8366 /* RudderTests-iOS.xctest */, + EDC1BC0D2B0DD87D00211F24 /* Rudder.framework */, + EDC1BC522B0DD88A00211F24 /* Rudder.framework */, + EDC1BC972B0DD89400211F24 /* Rudder.framework */, + EDC1BCB42B146A2D00211F24 /* RudderTests-watchOS.xctest */, + EDC1BCC82B146A3800211F24 /* RudderTests-tvOS.xctest */, + EDC1BCDC2B146A4E00211F24 /* RudderTests-macOS.xctest */, ); name = Products; sourceTree = ""; @@ -266,19 +531,11 @@ ED3D226327A3F0C100EC8366 /* Plugins */ = { isa = PBXGroup; children = ( - ED4EACAA27F5F03C00207AF1 /* RSAliasIdPlugin.swift */, - ED157C4027CFC33800F22202 /* RSAdvertisementIdPlugin.swift */, - ED157C4227CFC6F700F22202 /* RSAnonymousIdPlugin.swift */, - ED157C4827CFE6CE00F22202 /* RSAppTrackingConsentPlugin.swift */, ED3D227C27A3F0C100EC8366 /* RSContextPlugin.swift */, - ED3D227D27A3F0C100EC8366 /* RSDeviceTokenPlugin.swift */, - ED157C5327D0CA6900F22202 /* RSGDPRPlugin.swift */, - ED4EACA827F5EA1F00207AF1 /* RSIdentifyTraitsPlugin.swift */, ED157C4427CFD99700F22202 /* RSIntegrationPlugin.swift */, - ED3D227827A3F0C100EC8366 /* RSLoggerPlugin.swift */, - ED157C4627CFDEC000F22202 /* RSOptionPlugin.swift */, ED3D227A27A3F0C100EC8366 /* RSReplayQueuePlugin.swift */, - ED157C3827CE1C0800F22202 /* RSUserIdPlugin.swift */, + ED333EB62B21F324003EB0B3 /* RSSessionStoragePlugin.swift */, + 8C6EA4422A0BDADF0049569A /* RSUserSessionPlugin.swift */, ED3D227B27A3F0C100EC8366 /* RudderDestinationPlugin.swift */, ); path = Plugins; @@ -353,7 +610,7 @@ isa = PBXGroup; children = ( ED3D22BC27A4565A00EC8366 /* Classes */, - EDA7EF4E2739119600E73142 /* SDKInfo */, + EDA7EF4F2739119600E73142 /* Rudder.h */, ); path = Sources; sourceTree = ""; @@ -379,10 +636,10 @@ EDA7EF442739119600E73142 /* Constants */ = { isa = PBXGroup; children = ( + ED5372452B1613CE00D794EB /* LogMessages.swift */, EDA7EF462739119600E73142 /* RSConstants.swift */, ED277FA027421B06002A8FEA /* RSEventsAndKeys.swift */, ED3D22BD27A45B6500EC8366 /* RSTypeAlias.swift */, - EDA7EF452739119600E73142 /* RSUserDefaults.swift */, ED05631E291BEB0400BAEE65 /* RSVersion.swift */, ); path = Constants; @@ -391,27 +648,20 @@ EDA7EF4C2739119600E73142 /* Utility */ = { isa = PBXGroup; children = ( + EDC1BC982B10A26700211F24 /* Fixtures */, + ED53724A2B18ED8E00D794EB /* MockURLProtocol.swift */, EDA7EF4D2739119600E73142 /* RSUtils.swift */, + EDC1BC992B10A31C00211F24 /* TestUtils.swift */, ); path = Utility; sourceTree = ""; }; - EDA7EF4E2739119600E73142 /* SDKInfo */ = { - isa = PBXGroup; - children = ( - EDA7EF4F2739119600E73142 /* Rudder.h */, - EDA7EF502739119600E73142 /* Info.plist */, - ); - path = SDKInfo; - sourceTree = ""; - }; EDA7EF522739119600E73142 /* Enums */ = { isa = PBXGroup; children = ( EDC132A127D7D61D00AFD833 /* JSON.swift */, ED157C4A27CFE86100F22202 /* RSAppTrackingConsent.swift */, EDA7EF552739119600E73142 /* RSErrorCode.swift */, - EDA7EF562739119600E73142 /* RSLogLevel.swift */, EDA7EF532739119600E73142 /* RSMessageType.swift */, ); path = Enums; @@ -428,34 +678,29 @@ path = Attributes; sourceTree = ""; }; - EDC1325A27D612B600AFD833 /* Tests */ = { - isa = PBXGroup; - children = ( - EDC1325B27D612B600AFD833 /* RudderTests */, - ); - path = Tests; - sourceTree = ""; - }; EDC1325B27D612B600AFD833 /* RudderTests */ = { isa = PBXGroup; children = ( - EDC132A927D8D63D00AFD833 /* RSAliasTests.swift */, EDC132AD27D9104700AFD833 /* RSBlackListedEventsTest.swift */, EDC1327B27D614C400AFD833 /* RSClientTests.swift */, EDD0C9662829199200470E88 /* RSDatabaseTests.swift */, - EDC132AB27D8D64E00AFD833 /* RSGroupTests.swift */, - EDC132A527D8D5F300AFD833 /* RSIdentifyTests.swift */, EDF3FF54284895F9002B2D46 /* RSiOSLifeCycleEventTests.swift */, EDF3FF5728489E2C002B2D46 /* RSmacOSLifeCycleEventTests.swift */, - EDC132A727D8D61700AFD833 /* RSScreenTests.swift */, EDD0C96428225BFB00470E88 /* RSThreadTests.swift */, - EDC132A327D8D55600AFD833 /* RSTrackTests.swift */, EDF3FF5628489E2C002B2D46 /* RSwatchOSLifeCycleEventTests.swift */, EDC132AF27D9106500AFD833 /* RSWhiteListedEventsTests.swift */, ); path = RudderTests; sourceTree = ""; }; + EDC1BC982B10A26700211F24 /* Fixtures */ = { + isa = PBXGroup; + children = ( + EDC1BC9B2B10A5DA00211F24 /* ServerConfig.json */, + ); + path = Fixtures; + sourceTree = ""; + }; EDD3AA1727C656F90048E61E /* Domain */ = { isa = PBXGroup; children = ( @@ -469,9 +714,7 @@ EDD3AA1827C657210048E61E /* Protocols */ = { isa = PBXGroup; children = ( - ED3D227927A3F0C100EC8366 /* RSConsoleLogger.swift */, ED157C5727D0FFA400F22202 /* RSLifeCycle.swift */, - ED3D227727A3F0C100EC8366 /* RSLogger.swift */, ED3D229227A3F0C100EC8366 /* RSMessage.swift */, ED3D228127A3F0C100EC8366 /* RSPlugins.swift */, ED157C5927D1010D00F22202 /* RSPushNotifications.swift */, @@ -495,9 +738,9 @@ EDD3AA1B27C657EA0048E61E /* Extensions */ = { isa = PBXGroup; children = ( + ED560388288316C2004B5BEC /* Data+Ext.swift */, EDA7EF492739119600E73142 /* NSError+Ext.swift */, EDA7EF482739119600E73142 /* String+Ext.swift */, - EDA7EF4A2739119600E73142 /* UserDefaults+Ext.swift */, ); path = Extensions; sourceTree = ""; @@ -508,9 +751,11 @@ EDA7EF602739119600E73142 /* Attributes */, EDD3AA3527C659C80048E61E /* RSAtomic.swift */, EDA7EF592739119600E73142 /* RSConfig.swift */, + ED333EBB2B22055D003EB0B3 /* RSContext.swift */, EDA7EF582739119600E73142 /* RSDBMessage.swift */, EDD3AA1227C64B430048E61E /* RSKeyPath.swift */, EDA7EF5D2739119600E73142 /* RSOption.swift */, + ED5603842881C691004B5BEC /* RSUserInfo.swift */, ); path = Models; sourceTree = ""; @@ -528,10 +773,10 @@ isa = PBXGroup; children = ( ED3D226427A3F0C100EC8366 /* Platforms */, - ED277FF82744F199002A8FEA /* RSAnyCodable.swift */, - ED277FFA2744F265002A8FEA /* RSAnyDecodable.swift */, - ED277FFC2744F2AD002A8FEA /* RSAnyEncodable.swift */, + EDC1BCDD2B14738F00211F24 /* Logger.swift */, EDC14DDD2859C7DB00E2E6FE /* RSRepeatingTimer.swift */, + ED5603862882CE6D004B5BEC /* RSSessionStorage.swift */, + EDA7EF452739119600E73142 /* RSUserDefaults.swift */, ); path = Helpers; sourceTree = ""; @@ -547,12 +792,36 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EDC1BBCA2B0DD87D00211F24 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BBCB2B0DD87D00211F24 /* Rudder.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BC0F2B0DD88A00211F24 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BC102B0DD88A00211F24 /* Rudder.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BC542B0DD89400211F24 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BC552B0DD89400211F24 /* Rudder.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 06CABB832630C3CA0097BEFF /* Rudder */ = { + 06CABB832630C3CA0097BEFF /* Rudder-iOS */ = { isa = PBXNativeTarget; - buildConfigurationList = 06CABB982630C3CA0097BEFF /* Build configuration list for PBXNativeTarget "Rudder" */; + buildConfigurationList = 06CABB982630C3CA0097BEFF /* Build configuration list for PBXNativeTarget "Rudder-iOS" */; buildPhases = ( 06CABB7F2630C3CA0097BEFF /* Headers */, 06CABB802630C3CA0097BEFF /* Sources */, @@ -564,14 +833,14 @@ ); dependencies = ( ); - name = Rudder; + name = "Rudder-iOS"; productName = Rudder; productReference = 06CABB842630C3CA0097BEFF /* Rudder.framework */; productType = "com.apple.product-type.framework"; }; - ED3D2256279EAC2800EC8366 /* RudderTests */ = { + ED3D2256279EAC2800EC8366 /* RudderTests-iOS */ = { isa = PBXNativeTarget; - buildConfigurationList = ED3D225E279EAC2800EC8366 /* Build configuration list for PBXNativeTarget "RudderTests" */; + buildConfigurationList = ED3D225E279EAC2800EC8366 /* Build configuration list for PBXNativeTarget "RudderTests-iOS" */; buildPhases = ( ED3D2253279EAC2800EC8366 /* Sources */, ED3D2254279EAC2800EC8366 /* Frameworks */, @@ -582,9 +851,120 @@ dependencies = ( ED3D225D279EAC2800EC8366 /* PBXTargetDependency */, ); - name = RudderTests; + name = "RudderTests-iOS"; + productName = RudderTests; + productReference = ED3D2257279EAC2800EC8366 /* RudderTests-iOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + EDC1BBC92B0DD87D00211F24 /* Rudder-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = EDC1BC0A2B0DD87D00211F24 /* Build configuration list for PBXNativeTarget "Rudder-macOS" */; + buildPhases = ( + EDC1BBCA2B0DD87D00211F24 /* Headers */, + EDC1BBCC2B0DD87D00211F24 /* Sources */, + EDC1BC072B0DD87D00211F24 /* Frameworks */, + EDC1BC082B0DD87D00211F24 /* Resources */, + EDC1BC092B0DD87D00211F24 /* SwiftLint Script */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Rudder-macOS"; + productName = Rudder; + productReference = EDC1BC0D2B0DD87D00211F24 /* Rudder.framework */; + productType = "com.apple.product-type.framework"; + }; + EDC1BC0E2B0DD88A00211F24 /* Rudder-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = EDC1BC4F2B0DD88A00211F24 /* Build configuration list for PBXNativeTarget "Rudder-tvOS" */; + buildPhases = ( + EDC1BC0F2B0DD88A00211F24 /* Headers */, + EDC1BC112B0DD88A00211F24 /* Sources */, + EDC1BC4C2B0DD88A00211F24 /* Frameworks */, + EDC1BC4D2B0DD88A00211F24 /* Resources */, + EDC1BC4E2B0DD88A00211F24 /* SwiftLint Script */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Rudder-tvOS"; + productName = Rudder; + productReference = EDC1BC522B0DD88A00211F24 /* Rudder.framework */; + productType = "com.apple.product-type.framework"; + }; + EDC1BC532B0DD89400211F24 /* Rudder-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = EDC1BC942B0DD89400211F24 /* Build configuration list for PBXNativeTarget "Rudder-watchOS" */; + buildPhases = ( + EDC1BC542B0DD89400211F24 /* Headers */, + EDC1BC562B0DD89400211F24 /* Sources */, + EDC1BC912B0DD89400211F24 /* Frameworks */, + EDC1BC922B0DD89400211F24 /* Resources */, + EDC1BC932B0DD89400211F24 /* SwiftLint Script */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Rudder-watchOS"; + productName = Rudder; + productReference = EDC1BC972B0DD89400211F24 /* Rudder.framework */; + productType = "com.apple.product-type.framework"; + }; + EDC1BCA12B146A2D00211F24 /* RudderTests-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = EDC1BCB12B146A2D00211F24 /* Build configuration list for PBXNativeTarget "RudderTests-watchOS" */; + buildPhases = ( + EDC1BCA42B146A2D00211F24 /* Sources */, + EDC1BCAD2B146A2D00211F24 /* Frameworks */, + EDC1BCAF2B146A2D00211F24 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ED5372422B15C6C600D794EB /* PBXTargetDependency */, + ); + name = "RudderTests-watchOS"; + productName = RudderTests; + productReference = EDC1BCB42B146A2D00211F24 /* RudderTests-watchOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + EDC1BCB52B146A3800211F24 /* RudderTests-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = EDC1BCC52B146A3800211F24 /* Build configuration list for PBXNativeTarget "RudderTests-tvOS" */; + buildPhases = ( + EDC1BCB82B146A3800211F24 /* Sources */, + EDC1BCC12B146A3800211F24 /* Frameworks */, + EDC1BCC32B146A3800211F24 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ED5372402B15C6BC00D794EB /* PBXTargetDependency */, + ); + name = "RudderTests-tvOS"; + productName = RudderTests; + productReference = EDC1BCC82B146A3800211F24 /* RudderTests-tvOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + EDC1BCC92B146A4E00211F24 /* RudderTests-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = EDC1BCD92B146A4E00211F24 /* Build configuration list for PBXNativeTarget "RudderTests-macOS" */; + buildPhases = ( + EDC1BCCC2B146A4E00211F24 /* Sources */, + EDC1BCD52B146A4E00211F24 /* Frameworks */, + EDC1BCD72B146A4E00211F24 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ED5372442B15C6CE00D794EB /* PBXTargetDependency */, + ); + name = "RudderTests-macOS"; productName = RudderTests; - productReference = ED3D2257279EAC2800EC8366 /* RudderTests.xctest */; + productReference = EDC1BCDC2B146A4E00211F24 /* RudderTests-macOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -593,7 +973,7 @@ 06CABB7B2630C3CA0097BEFF /* Project object */ = { isa = PBXProject; attributes = { - CLASSPREFIX = RS; + CLASSPREFIX = ""; LastSwiftUpdateCheck = 1320; LastUpgradeCheck = 1310; ORGANIZATIONNAME = "Rudder Labs India Pvt Ltd."; @@ -620,8 +1000,14 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 06CABB832630C3CA0097BEFF /* Rudder */, - ED3D2256279EAC2800EC8366 /* RudderTests */, + 06CABB832630C3CA0097BEFF /* Rudder-iOS */, + EDC1BC0E2B0DD88A00211F24 /* Rudder-tvOS */, + EDC1BC532B0DD89400211F24 /* Rudder-watchOS */, + EDC1BBC92B0DD87D00211F24 /* Rudder-macOS */, + ED3D2256279EAC2800EC8366 /* RudderTests-iOS */, + EDC1BCB52B146A3800211F24 /* RudderTests-tvOS */, + EDC1BCA12B146A2D00211F24 /* RudderTests-watchOS */, + EDC1BCC92B146A4E00211F24 /* RudderTests-macOS */, ); }; /* End PBXProject section */ @@ -631,6 +1017,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + EDC1BCE22B15BC4600211F24 /* ServerConfig.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -641,6 +1028,51 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EDC1BC082B0DD87D00211F24 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BCE52B15BC4800211F24 /* ServerConfig.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BC4D2B0DD88A00211F24 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BCE32B15BC4700211F24 /* ServerConfig.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BC922B0DD89400211F24 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BCE42B15BC4800211F24 /* ServerConfig.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCAF2B146A2D00211F24 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCC32B146A3800211F24 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCD72B146A4E00211F24 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -662,6 +1094,60 @@ shellPath = /bin/sh; shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; + EDC1BC092B0DD87D00211F24 /* SwiftLint Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "SwiftLint Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; + EDC1BC4E2B0DD88A00211F24 /* SwiftLint Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "SwiftLint Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; + EDC1BC932B0DD89400211F24 /* SwiftLint Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "SwiftLint Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -670,52 +1156,44 @@ buildActionMask = 2147483647; files = ( 8C6EA4432A0BDADF0049569A /* RSUserSessionPlugin.swift in Sources */, - ED277FFD2744F2AD002A8FEA /* RSAnyEncodable.swift in Sources */, EDC132A227D7D61D00AFD833 /* JSON.swift in Sources */, ED3D229B27A3F0C100EC8366 /* RSiOSLifecycleEvents.swift in Sources */, EDA7EFA22739119600E73142 /* RSServiceType.swift in Sources */, EDA7EF872739119600E73142 /* NSError+Ext.swift in Sources */, - EDA7EF902739119600E73142 /* RSLogLevel.swift in Sources */, EDA7EF9A2739119600E73142 /* RSDestinationConfig.swift in Sources */, EDA7EF8D2739119600E73142 /* RSMessageType.swift in Sources */, ED3D22A527A3F0C100EC8366 /* RudderDestinationPlugin.swift in Sources */, + ED5372462B1613CE00D794EB /* LogMessages.swift in Sources */, ED3D229927A3F0C100EC8366 /* RSwatchOSLifecycleEvents.swift in Sources */, EDA7EF8F2739119600E73142 /* RSErrorCode.swift in Sources */, + ED5603872882CE6D004B5BEC /* RSSessionStorage.swift in Sources */, ED3D22BA27A3F0C100EC8366 /* RSMessage.swift in Sources */, ED3D229827A3F0C100EC8366 /* RSwatchOSDelegation.swift in Sources */, EDA7EF852739119600E73142 /* RSConstants.swift in Sources */, - EDA7EF882739119600E73142 /* UserDefaults+Ext.swift in Sources */, EDD3AA3627C659C80048E61E /* RSAtomic.swift in Sources */, + ED333EBC2B22055D003EB0B3 /* RSContext.swift in Sources */, ED3D22A427A3F0C100EC8366 /* RSReplayQueuePlugin.swift in Sources */, EDA7EF962739119600E73142 /* RSOption.swift in Sources */, - ED277FFB2744F265002A8FEA /* RSAnyDecodable.swift in Sources */, ED277FA127421B06002A8FEA /* RSEventsAndKeys.swift in Sources */, + ED53724B2B18ED8E00D794EB /* MockURLProtocol.swift in Sources */, ED157C4527CFD99700F22202 /* RSIntegrationPlugin.swift in Sources */, - ED157C4727CFDEC000F22202 /* RSOptionPlugin.swift in Sources */, EDC14DDE2859C7DB00E2E6FE /* RSRepeatingTimer.swift in Sources */, ED157C3727CE0EEA00F22202 /* RSmacOSLifecycleEvents.swift in Sources */, ED157C4B27CFE86100F22202 /* RSAppTrackingConsent.swift in Sources */, - ED157C4327CFC6F700F22202 /* RSAnonymousIdPlugin.swift in Sources */, EDA7EF922739119600E73142 /* RSConfig.swift in Sources */, - ED157C4927CFE6CE00F22202 /* RSAppTrackingConsentPlugin.swift in Sources */, EDD3AA1327C64B430048E61E /* RSKeyPath.swift in Sources */, EDA7EF752739119600E73142 /* API.swift in Sources */, EDA7EF812739119600E73142 /* RSDatabaseManager.swift in Sources */, - ED157C4127CFC33800F22202 /* RSAdvertisementIdPlugin.swift in Sources */, ED05631F291BEB0400BAEE65 /* RSVersion.swift in Sources */, EDA7EF992739119600E73142 /* RSServerConfig.swift in Sources */, - ED277FF92744F199002A8FEA /* RSAnyCodable.swift in Sources */, - ED157C3927CE1C0800F22202 /* RSUserIdPlugin.swift in Sources */, ED3D229E27A3F0C100EC8366 /* RSmacOSLifecycleMonitor.swift in Sources */, + EDC1BCA02B1465AC00211F24 /* TestUtils.swift in Sources */, ED3D229D27A3F0C100EC8366 /* RSiOSLifecycleMonitor.swift in Sources */, - ED4EACA927F5EA1F00207AF1 /* RSIdentifyTraitsPlugin.swift in Sources */, - EDD3AA4C27C8DABD0048E61E /* RSConsoleLogger.swift in Sources */, + ED5603852881C691004B5BEC /* RSUserInfo.swift in Sources */, ED157C5A27D1010D00F22202 /* RSPushNotifications.swift in Sources */, + ED333EB72B21F324003EB0B3 /* RSSessionStoragePlugin.swift in Sources */, ED3D22A827A3F0C100EC8366 /* RSController.swift in Sources */, - ED3D22A727A3F0C100EC8366 /* RSDeviceTokenPlugin.swift in Sources */, - EDD3AA4A27C8DAAA0048E61E /* RSLoggerPlugin.swift in Sources */, ED157C4F27D0AC0E00F22202 /* RSwatchOSScreenViewEvents.swift in Sources */, - EDD3AA4B27C8DAB90048E61E /* RSLogger.swift in Sources */, ED3D22BE27A45B6500EC8366 /* RSTypeAlias.swift in Sources */, ED157C4D27D0A63A00F22202 /* RSiOSScreenViewEvents.swift in Sources */, EDA7EF842739119600E73142 /* RSUserDefaults.swift in Sources */, @@ -724,18 +1202,18 @@ ED157C5827D0FFA400F22202 /* RSLifeCycle.swift in Sources */, EDA7EF862739119600E73142 /* String+Ext.swift in Sources */, ED3D22AB27A3F0C100EC8366 /* RSPlugins.swift in Sources */, - ED4EACAB27F5F03C00207AF1 /* RSAliasIdPlugin.swift in Sources */, EDA7EF9B2739119600E73142 /* RSDestinationDefinition.swift in Sources */, ED3D229427A3F0C100EC8366 /* RSClient.swift in Sources */, ED3D229627A3F0C100EC8366 /* AppleUtils.swift in Sources */, ED3D22AC27A3F0C100EC8366 /* RSClient+Plugins.swift in Sources */, + EDC1BCDE2B14738F00211F24 /* Logger.swift in Sources */, ED3D22A627A3F0C100EC8366 /* RSContextPlugin.swift in Sources */, + ED560389288316C2004B5BEC /* Data+Ext.swift in Sources */, EDA7EF762739119600E73142 /* RSServiceManager.swift in Sources */, EDA7EF912739119600E73142 /* RSDBMessage.swift in Sources */, EDA7EF9C2739119600E73142 /* RSDestination.swift in Sources */, ED3D229C27A3F0C100EC8366 /* RSiOSDelegation.swift in Sources */, ED3D229A27A3F0C100EC8366 /* RSwatchOSLifecycleMonitor.swift in Sources */, - ED157C5427D0CA6900F22202 /* RSGDPRPlugin.swift in Sources */, EDA7EF8A2739119600E73142 /* RSUtils.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -744,39 +1222,295 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - EDC132AA27D8D63D00AFD833 /* RSAliasTests.swift in Sources */, - EDC132A427D8D55700AFD833 /* RSTrackTests.swift in Sources */, EDC1327C27D614C400AFD833 /* RSClientTests.swift in Sources */, EDF3FF5928489E2C002B2D46 /* RSmacOSLifeCycleEventTests.swift in Sources */, - EDC132A827D8D61700AFD833 /* RSScreenTests.swift in Sources */, EDF3FF5828489E2C002B2D46 /* RSwatchOSLifeCycleEventTests.swift in Sources */, EDC132B027D9106500AFD833 /* RSWhiteListedEventsTests.swift in Sources */, - EDC132AC27D8D64E00AFD833 /* RSGroupTests.swift in Sources */, EDD0C96528225BFB00470E88 /* RSThreadTests.swift in Sources */, EDF3FF55284895F9002B2D46 /* RSiOSLifeCycleEventTests.swift in Sources */, EDC132AE27D9104700AFD833 /* RSBlackListedEventsTest.swift in Sources */, - EDC132A627D8D5F300AFD833 /* RSIdentifyTests.swift in Sources */, EDD0C9672829199200470E88 /* RSDatabaseTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - ED3D225D279EAC2800EC8366 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 06CABB832630C3CA0097BEFF /* Rudder */; - targetProxy = ED3D225C279EAC2800EC8366 /* PBXContainerItemProxy */; + EDC1BBCC2B0DD87D00211F24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BBCD2B0DD87D00211F24 /* RSUserSessionPlugin.swift in Sources */, + EDC1BBCE2B0DD87D00211F24 /* JSON.swift in Sources */, + EDC1BBCF2B0DD87D00211F24 /* RSiOSLifecycleEvents.swift in Sources */, + EDC1BBD02B0DD87D00211F24 /* RSServiceType.swift in Sources */, + EDC1BBD12B0DD87D00211F24 /* NSError+Ext.swift in Sources */, + EDC1BBD32B0DD87D00211F24 /* RSDestinationConfig.swift in Sources */, + EDC1BBD42B0DD87D00211F24 /* RSMessageType.swift in Sources */, + EDC1BBD52B0DD87D00211F24 /* RudderDestinationPlugin.swift in Sources */, + ED5372492B1613CE00D794EB /* LogMessages.swift in Sources */, + EDC1BBD62B0DD87D00211F24 /* RSwatchOSLifecycleEvents.swift in Sources */, + EDC1BBD72B0DD87D00211F24 /* RSErrorCode.swift in Sources */, + EDC1BBD82B0DD87D00211F24 /* RSSessionStorage.swift in Sources */, + EDC1BBD92B0DD87D00211F24 /* RSMessage.swift in Sources */, + EDC1BBDA2B0DD87D00211F24 /* RSwatchOSDelegation.swift in Sources */, + EDC1BBDB2B0DD87D00211F24 /* RSConstants.swift in Sources */, + EDC1BBDC2B0DD87D00211F24 /* RSAtomic.swift in Sources */, + ED333EBF2B22055D003EB0B3 /* RSContext.swift in Sources */, + EDC1BBDD2B0DD87D00211F24 /* RSReplayQueuePlugin.swift in Sources */, + EDC1BBDE2B0DD87D00211F24 /* RSOption.swift in Sources */, + EDC1BBDF2B0DD87D00211F24 /* RSEventsAndKeys.swift in Sources */, + ED53724E2B18ED8E00D794EB /* MockURLProtocol.swift in Sources */, + EDC1BBE02B0DD87D00211F24 /* RSIntegrationPlugin.swift in Sources */, + EDC1BBE12B0DD87D00211F24 /* RSRepeatingTimer.swift in Sources */, + EDC1BBE22B0DD87D00211F24 /* RSmacOSLifecycleEvents.swift in Sources */, + EDC1BBE32B0DD87D00211F24 /* RSAppTrackingConsent.swift in Sources */, + EDC1BBE42B0DD87D00211F24 /* RSConfig.swift in Sources */, + EDC1BBE52B0DD87D00211F24 /* RSKeyPath.swift in Sources */, + EDC1BBE62B0DD87D00211F24 /* API.swift in Sources */, + EDC1BBE72B0DD87D00211F24 /* RSDatabaseManager.swift in Sources */, + EDC1BBE82B0DD87D00211F24 /* RSVersion.swift in Sources */, + EDC1BBE92B0DD87D00211F24 /* RSServerConfig.swift in Sources */, + EDC1BBEA2B0DD87D00211F24 /* RSmacOSLifecycleMonitor.swift in Sources */, + EDC1BC9D2B1465AC00211F24 /* TestUtils.swift in Sources */, + EDC1BBEB2B0DD87D00211F24 /* RSiOSLifecycleMonitor.swift in Sources */, + EDC1BBEC2B0DD87D00211F24 /* RSUserInfo.swift in Sources */, + EDC1BBEE2B0DD87D00211F24 /* RSPushNotifications.swift in Sources */, + ED333EBA2B21F324003EB0B3 /* RSSessionStoragePlugin.swift in Sources */, + EDC1BBEF2B0DD87D00211F24 /* RSController.swift in Sources */, + EDC1BBF12B0DD87D00211F24 /* RSwatchOSScreenViewEvents.swift in Sources */, + EDC1BBF32B0DD87D00211F24 /* RSTypeAlias.swift in Sources */, + EDC1BBF42B0DD87D00211F24 /* RSiOSScreenViewEvents.swift in Sources */, + EDC1BBF52B0DD87D00211F24 /* RSUserDefaults.swift in Sources */, + EDC1BBF62B0DD87D00211F24 /* RSmacOSScreenViewEvents.swift in Sources */, + EDC1BBF72B0DD87D00211F24 /* Vendor.swift in Sources */, + EDC1BBF82B0DD87D00211F24 /* RSLifeCycle.swift in Sources */, + EDC1BBF92B0DD87D00211F24 /* String+Ext.swift in Sources */, + EDC1BBFA2B0DD87D00211F24 /* RSPlugins.swift in Sources */, + EDC1BBFB2B0DD87D00211F24 /* RSDestinationDefinition.swift in Sources */, + EDC1BBFC2B0DD87D00211F24 /* RSClient.swift in Sources */, + EDC1BBFD2B0DD87D00211F24 /* AppleUtils.swift in Sources */, + EDC1BBFE2B0DD87D00211F24 /* RSClient+Plugins.swift in Sources */, + EDC1BCE12B14738F00211F24 /* Logger.swift in Sources */, + EDC1BBFF2B0DD87D00211F24 /* RSContextPlugin.swift in Sources */, + EDC1BC002B0DD87D00211F24 /* Data+Ext.swift in Sources */, + EDC1BC012B0DD87D00211F24 /* RSServiceManager.swift in Sources */, + EDC1BC022B0DD87D00211F24 /* RSDBMessage.swift in Sources */, + EDC1BC032B0DD87D00211F24 /* RSDestination.swift in Sources */, + EDC1BC042B0DD87D00211F24 /* RSiOSDelegation.swift in Sources */, + EDC1BC052B0DD87D00211F24 /* RSwatchOSLifecycleMonitor.swift in Sources */, + EDC1BC062B0DD87D00211F24 /* RSUtils.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 06CABB962630C3CA0097BEFF /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + EDC1BC112B0DD88A00211F24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BC122B0DD88A00211F24 /* RSUserSessionPlugin.swift in Sources */, + EDC1BC132B0DD88A00211F24 /* JSON.swift in Sources */, + EDC1BC142B0DD88A00211F24 /* RSiOSLifecycleEvents.swift in Sources */, + EDC1BC152B0DD88A00211F24 /* RSServiceType.swift in Sources */, + EDC1BC162B0DD88A00211F24 /* NSError+Ext.swift in Sources */, + EDC1BC182B0DD88A00211F24 /* RSDestinationConfig.swift in Sources */, + EDC1BC192B0DD88A00211F24 /* RSMessageType.swift in Sources */, + EDC1BC1A2B0DD88A00211F24 /* RudderDestinationPlugin.swift in Sources */, + ED5372472B1613CE00D794EB /* LogMessages.swift in Sources */, + EDC1BC1B2B0DD88A00211F24 /* RSwatchOSLifecycleEvents.swift in Sources */, + EDC1BC1C2B0DD88A00211F24 /* RSErrorCode.swift in Sources */, + EDC1BC1D2B0DD88A00211F24 /* RSSessionStorage.swift in Sources */, + EDC1BC1E2B0DD88A00211F24 /* RSMessage.swift in Sources */, + EDC1BC1F2B0DD88A00211F24 /* RSwatchOSDelegation.swift in Sources */, + EDC1BC202B0DD88A00211F24 /* RSConstants.swift in Sources */, + EDC1BC212B0DD88A00211F24 /* RSAtomic.swift in Sources */, + ED333EBD2B22055D003EB0B3 /* RSContext.swift in Sources */, + EDC1BC222B0DD88A00211F24 /* RSReplayQueuePlugin.swift in Sources */, + EDC1BC232B0DD88A00211F24 /* RSOption.swift in Sources */, + EDC1BC242B0DD88A00211F24 /* RSEventsAndKeys.swift in Sources */, + ED53724C2B18ED8E00D794EB /* MockURLProtocol.swift in Sources */, + EDC1BC252B0DD88A00211F24 /* RSIntegrationPlugin.swift in Sources */, + EDC1BC262B0DD88A00211F24 /* RSRepeatingTimer.swift in Sources */, + EDC1BC272B0DD88A00211F24 /* RSmacOSLifecycleEvents.swift in Sources */, + EDC1BC282B0DD88A00211F24 /* RSAppTrackingConsent.swift in Sources */, + EDC1BC292B0DD88A00211F24 /* RSConfig.swift in Sources */, + EDC1BC2A2B0DD88A00211F24 /* RSKeyPath.swift in Sources */, + EDC1BC2B2B0DD88A00211F24 /* API.swift in Sources */, + EDC1BC2C2B0DD88A00211F24 /* RSDatabaseManager.swift in Sources */, + EDC1BC2D2B0DD88A00211F24 /* RSVersion.swift in Sources */, + EDC1BC2E2B0DD88A00211F24 /* RSServerConfig.swift in Sources */, + EDC1BC2F2B0DD88A00211F24 /* RSmacOSLifecycleMonitor.swift in Sources */, + EDC1BC9E2B1465AC00211F24 /* TestUtils.swift in Sources */, + EDC1BC302B0DD88A00211F24 /* RSiOSLifecycleMonitor.swift in Sources */, + EDC1BC312B0DD88A00211F24 /* RSUserInfo.swift in Sources */, + EDC1BC332B0DD88A00211F24 /* RSPushNotifications.swift in Sources */, + ED333EB82B21F324003EB0B3 /* RSSessionStoragePlugin.swift in Sources */, + EDC1BC342B0DD88A00211F24 /* RSController.swift in Sources */, + EDC1BC362B0DD88A00211F24 /* RSwatchOSScreenViewEvents.swift in Sources */, + EDC1BC382B0DD88A00211F24 /* RSTypeAlias.swift in Sources */, + EDC1BC392B0DD88A00211F24 /* RSiOSScreenViewEvents.swift in Sources */, + EDC1BC3A2B0DD88A00211F24 /* RSUserDefaults.swift in Sources */, + EDC1BC3B2B0DD88A00211F24 /* RSmacOSScreenViewEvents.swift in Sources */, + EDC1BC3C2B0DD88A00211F24 /* Vendor.swift in Sources */, + EDC1BC3D2B0DD88A00211F24 /* RSLifeCycle.swift in Sources */, + EDC1BC3E2B0DD88A00211F24 /* String+Ext.swift in Sources */, + EDC1BC3F2B0DD88A00211F24 /* RSPlugins.swift in Sources */, + EDC1BC402B0DD88A00211F24 /* RSDestinationDefinition.swift in Sources */, + EDC1BC412B0DD88A00211F24 /* RSClient.swift in Sources */, + EDC1BC422B0DD88A00211F24 /* AppleUtils.swift in Sources */, + EDC1BC432B0DD88A00211F24 /* RSClient+Plugins.swift in Sources */, + EDC1BCDF2B14738F00211F24 /* Logger.swift in Sources */, + EDC1BC442B0DD88A00211F24 /* RSContextPlugin.swift in Sources */, + EDC1BC452B0DD88A00211F24 /* Data+Ext.swift in Sources */, + EDC1BC462B0DD88A00211F24 /* RSServiceManager.swift in Sources */, + EDC1BC472B0DD88A00211F24 /* RSDBMessage.swift in Sources */, + EDC1BC482B0DD88A00211F24 /* RSDestination.swift in Sources */, + EDC1BC492B0DD88A00211F24 /* RSiOSDelegation.swift in Sources */, + EDC1BC4A2B0DD88A00211F24 /* RSwatchOSLifecycleMonitor.swift in Sources */, + EDC1BC4B2B0DD88A00211F24 /* RSUtils.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BC562B0DD89400211F24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BC572B0DD89400211F24 /* RSUserSessionPlugin.swift in Sources */, + EDC1BC582B0DD89400211F24 /* JSON.swift in Sources */, + EDC1BC592B0DD89400211F24 /* RSiOSLifecycleEvents.swift in Sources */, + EDC1BC5A2B0DD89400211F24 /* RSServiceType.swift in Sources */, + EDC1BC5B2B0DD89400211F24 /* NSError+Ext.swift in Sources */, + EDC1BC5D2B0DD89400211F24 /* RSDestinationConfig.swift in Sources */, + EDC1BC5E2B0DD89400211F24 /* RSMessageType.swift in Sources */, + EDC1BC5F2B0DD89400211F24 /* RudderDestinationPlugin.swift in Sources */, + ED5372482B1613CE00D794EB /* LogMessages.swift in Sources */, + EDC1BC602B0DD89400211F24 /* RSwatchOSLifecycleEvents.swift in Sources */, + EDC1BC612B0DD89400211F24 /* RSErrorCode.swift in Sources */, + EDC1BC622B0DD89400211F24 /* RSSessionStorage.swift in Sources */, + EDC1BC632B0DD89400211F24 /* RSMessage.swift in Sources */, + EDC1BC642B0DD89400211F24 /* RSwatchOSDelegation.swift in Sources */, + EDC1BC652B0DD89400211F24 /* RSConstants.swift in Sources */, + EDC1BC662B0DD89400211F24 /* RSAtomic.swift in Sources */, + ED333EBE2B22055D003EB0B3 /* RSContext.swift in Sources */, + EDC1BC672B0DD89400211F24 /* RSReplayQueuePlugin.swift in Sources */, + EDC1BC682B0DD89400211F24 /* RSOption.swift in Sources */, + EDC1BC692B0DD89400211F24 /* RSEventsAndKeys.swift in Sources */, + ED53724D2B18ED8E00D794EB /* MockURLProtocol.swift in Sources */, + EDC1BC6A2B0DD89400211F24 /* RSIntegrationPlugin.swift in Sources */, + EDC1BC6B2B0DD89400211F24 /* RSRepeatingTimer.swift in Sources */, + EDC1BC6C2B0DD89400211F24 /* RSmacOSLifecycleEvents.swift in Sources */, + EDC1BC6D2B0DD89400211F24 /* RSAppTrackingConsent.swift in Sources */, + EDC1BC6E2B0DD89400211F24 /* RSConfig.swift in Sources */, + EDC1BC6F2B0DD89400211F24 /* RSKeyPath.swift in Sources */, + EDC1BC702B0DD89400211F24 /* API.swift in Sources */, + EDC1BC712B0DD89400211F24 /* RSDatabaseManager.swift in Sources */, + EDC1BC722B0DD89400211F24 /* RSVersion.swift in Sources */, + EDC1BC732B0DD89400211F24 /* RSServerConfig.swift in Sources */, + EDC1BC742B0DD89400211F24 /* RSmacOSLifecycleMonitor.swift in Sources */, + EDC1BC9F2B1465AC00211F24 /* TestUtils.swift in Sources */, + EDC1BC752B0DD89400211F24 /* RSiOSLifecycleMonitor.swift in Sources */, + EDC1BC762B0DD89400211F24 /* RSUserInfo.swift in Sources */, + EDC1BC782B0DD89400211F24 /* RSPushNotifications.swift in Sources */, + ED333EB92B21F324003EB0B3 /* RSSessionStoragePlugin.swift in Sources */, + EDC1BC792B0DD89400211F24 /* RSController.swift in Sources */, + EDC1BC7B2B0DD89400211F24 /* RSwatchOSScreenViewEvents.swift in Sources */, + EDC1BC7D2B0DD89400211F24 /* RSTypeAlias.swift in Sources */, + EDC1BC7E2B0DD89400211F24 /* RSiOSScreenViewEvents.swift in Sources */, + EDC1BC7F2B0DD89400211F24 /* RSUserDefaults.swift in Sources */, + EDC1BC802B0DD89400211F24 /* RSmacOSScreenViewEvents.swift in Sources */, + EDC1BC812B0DD89400211F24 /* Vendor.swift in Sources */, + EDC1BC822B0DD89400211F24 /* RSLifeCycle.swift in Sources */, + EDC1BC832B0DD89400211F24 /* String+Ext.swift in Sources */, + EDC1BC842B0DD89400211F24 /* RSPlugins.swift in Sources */, + EDC1BC852B0DD89400211F24 /* RSDestinationDefinition.swift in Sources */, + EDC1BC862B0DD89400211F24 /* RSClient.swift in Sources */, + EDC1BC872B0DD89400211F24 /* AppleUtils.swift in Sources */, + EDC1BC882B0DD89400211F24 /* RSClient+Plugins.swift in Sources */, + EDC1BCE02B14738F00211F24 /* Logger.swift in Sources */, + EDC1BC892B0DD89400211F24 /* RSContextPlugin.swift in Sources */, + EDC1BC8A2B0DD89400211F24 /* Data+Ext.swift in Sources */, + EDC1BC8B2B0DD89400211F24 /* RSServiceManager.swift in Sources */, + EDC1BC8C2B0DD89400211F24 /* RSDBMessage.swift in Sources */, + EDC1BC8D2B0DD89400211F24 /* RSDestination.swift in Sources */, + EDC1BC8E2B0DD89400211F24 /* RSiOSDelegation.swift in Sources */, + EDC1BC8F2B0DD89400211F24 /* RSwatchOSLifecycleMonitor.swift in Sources */, + EDC1BC902B0DD89400211F24 /* RSUtils.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCA42B146A2D00211F24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BCA52B146A2D00211F24 /* RSClientTests.swift in Sources */, + EDC1BCA62B146A2D00211F24 /* RSmacOSLifeCycleEventTests.swift in Sources */, + EDC1BCA72B146A2D00211F24 /* RSwatchOSLifeCycleEventTests.swift in Sources */, + EDC1BCA82B146A2D00211F24 /* RSWhiteListedEventsTests.swift in Sources */, + EDC1BCA92B146A2D00211F24 /* RSThreadTests.swift in Sources */, + EDC1BCAA2B146A2D00211F24 /* RSiOSLifeCycleEventTests.swift in Sources */, + EDC1BCAB2B146A2D00211F24 /* RSBlackListedEventsTest.swift in Sources */, + EDC1BCAC2B146A2D00211F24 /* RSDatabaseTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCB82B146A3800211F24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BCB92B146A3800211F24 /* RSClientTests.swift in Sources */, + EDC1BCBA2B146A3800211F24 /* RSmacOSLifeCycleEventTests.swift in Sources */, + EDC1BCBB2B146A3800211F24 /* RSwatchOSLifeCycleEventTests.swift in Sources */, + EDC1BCBC2B146A3800211F24 /* RSWhiteListedEventsTests.swift in Sources */, + EDC1BCBD2B146A3800211F24 /* RSThreadTests.swift in Sources */, + EDC1BCBE2B146A3800211F24 /* RSiOSLifeCycleEventTests.swift in Sources */, + EDC1BCBF2B146A3800211F24 /* RSBlackListedEventsTest.swift in Sources */, + EDC1BCC02B146A3800211F24 /* RSDatabaseTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDC1BCCC2B146A4E00211F24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC1BCCD2B146A4E00211F24 /* RSClientTests.swift in Sources */, + EDC1BCCE2B146A4E00211F24 /* RSmacOSLifeCycleEventTests.swift in Sources */, + EDC1BCCF2B146A4E00211F24 /* RSwatchOSLifeCycleEventTests.swift in Sources */, + EDC1BCD02B146A4E00211F24 /* RSWhiteListedEventsTests.swift in Sources */, + EDC1BCD12B146A4E00211F24 /* RSThreadTests.swift in Sources */, + EDC1BCD22B146A4E00211F24 /* RSiOSLifeCycleEventTests.swift in Sources */, + EDC1BCD32B146A4E00211F24 /* RSBlackListedEventsTest.swift in Sources */, + EDC1BCD42B146A4E00211F24 /* RSDatabaseTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + ED3D225D279EAC2800EC8366 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 06CABB832630C3CA0097BEFF /* Rudder-iOS */; + targetProxy = ED3D225C279EAC2800EC8366 /* PBXContainerItemProxy */; + }; + ED5372402B15C6BC00D794EB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = EDC1BC0E2B0DD88A00211F24 /* Rudder-tvOS */; + targetProxy = ED53723F2B15C6BC00D794EB /* PBXContainerItemProxy */; + }; + ED5372422B15C6C600D794EB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = EDC1BC532B0DD89400211F24 /* Rudder-watchOS */; + targetProxy = ED5372412B15C6C600D794EB /* PBXContainerItemProxy */; + }; + ED5372442B15C6CE00D794EB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = EDC1BBC92B0DD87D00211F24 /* Rudder-macOS */; + targetProxy = ED5372432B15C6CE00D794EB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 06CABB962630C3CA0097BEFF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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; @@ -909,7 +1643,8 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "$(SRCROOT)/Sources/SDKInfo/Info.plist"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -917,16 +1652,16 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.4; + MARKETING_VERSION = 2.2.7; PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderStack; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PRODUCT_NAME = Rudder; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos watchsimulator watchos macosx"; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SUPPORTS_MACCATALYST = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3,4,6"; + TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; @@ -945,7 +1680,8 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_TESTABILITY = NO; - INFOPLIST_FILE = "$(SRCROOT)/Sources/SDKInfo/Info.plist"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -953,15 +1689,15 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.4; + MARKETING_VERSION = 2.2.7; PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderStack; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PRODUCT_NAME = Rudder; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos watchsimulator watchos macosx"; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3,4,6"; + TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; @@ -974,16 +1710,16 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = GTGKNDBD23; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.2; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos watchsimulator watchos macosx"; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3,4,6"; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -995,11 +1731,362 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = GTGKNDBD23; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.2; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos watchsimulator watchos macosx"; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + EDC1BC0B2B0DD87D00211F24 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 8; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 2.2.7; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderStack; + PRODUCT_NAME = Rudder; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = macosx; + SUPPORTS_MACCATALYST = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,4,6"; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Debug; + }; + EDC1BC0C2B0DD87D00211F24 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 8; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_TESTABILITY = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 2.2.7; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderStack; + PRODUCT_NAME = Rudder; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = macosx; + SUPPORTS_MACCATALYST = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,4,6"; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Release; + }; + EDC1BC502B0DD88A00211F24 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 8; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 2.2.7; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderStack; + PRODUCT_NAME = Rudder; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; + SUPPORTS_MACCATALYST = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Debug; + }; + EDC1BC512B0DD88A00211F24 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 8; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_TESTABILITY = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 2.2.7; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderStack; + PRODUCT_NAME = Rudder; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; + SUPPORTS_MACCATALYST = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Release; + }; + EDC1BC952B0DD89400211F24 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 8; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 2.2.7; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderStack; + PRODUCT_NAME = Rudder; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "watchsimulator watchos"; + SUPPORTS_MACCATALYST = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Debug; + }; + EDC1BC962B0DD89400211F24 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 8; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_TESTABILITY = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 2.2.7; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderStack; + PRODUCT_NAME = Rudder; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "watchsimulator watchos"; + SUPPORTS_MACCATALYST = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Release; + }; + EDC1BCB22B146A2D00211F24 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GTGKNDBD23; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SUPPORTED_PLATFORMS = "watchsimulator watchos"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + }; + name = Debug; + }; + EDC1BCB32B146A2D00211F24 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GTGKNDBD23; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SUPPORTED_PLATFORMS = "watchsimulator watchos"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + }; + name = Release; + }; + EDC1BCC62B146A3800211F24 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GTGKNDBD23; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + }; + name = Debug; + }; + EDC1BCC72B146A3800211F24 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GTGKNDBD23; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + }; + name = Release; + }; + EDC1BCDA2B146A4E00211F24 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GTGKNDBD23; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,4,6"; + }; + name = Debug; + }; + EDC1BCDB2B146A4E00211F24 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GTGKNDBD23; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = macosx; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -1019,7 +2106,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 06CABB982630C3CA0097BEFF /* Build configuration list for PBXNativeTarget "Rudder" */ = { + 06CABB982630C3CA0097BEFF /* Build configuration list for PBXNativeTarget "Rudder-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 06CABB992630C3CA0097BEFF /* Debug */, @@ -1028,7 +2115,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - ED3D225E279EAC2800EC8366 /* Build configuration list for PBXNativeTarget "RudderTests" */ = { + ED3D225E279EAC2800EC8366 /* Build configuration list for PBXNativeTarget "RudderTests-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( ED3D225F279EAC2800EC8366 /* Debug */, @@ -1037,6 +2124,60 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + EDC1BC0A2B0DD87D00211F24 /* Build configuration list for PBXNativeTarget "Rudder-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EDC1BC0B2B0DD87D00211F24 /* Debug */, + EDC1BC0C2B0DD87D00211F24 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EDC1BC4F2B0DD88A00211F24 /* Build configuration list for PBXNativeTarget "Rudder-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EDC1BC502B0DD88A00211F24 /* Debug */, + EDC1BC512B0DD88A00211F24 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EDC1BC942B0DD89400211F24 /* Build configuration list for PBXNativeTarget "Rudder-watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EDC1BC952B0DD89400211F24 /* Debug */, + EDC1BC962B0DD89400211F24 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EDC1BCB12B146A2D00211F24 /* Build configuration list for PBXNativeTarget "RudderTests-watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EDC1BCB22B146A2D00211F24 /* Debug */, + EDC1BCB32B146A2D00211F24 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EDC1BCC52B146A3800211F24 /* Build configuration list for PBXNativeTarget "RudderTests-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EDC1BCC62B146A3800211F24 /* Debug */, + EDC1BCC72B146A3800211F24 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EDC1BCD92B146A4E00211F24 /* Build configuration list for PBXNativeTarget "RudderTests-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EDC1BCDA2B146A4E00211F24 /* Debug */, + EDC1BCDB2B146A4E00211F24 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 06CABB7B2630C3CA0097BEFF /* Project object */; diff --git a/Rudder.xcodeproj/xcshareddata/xcschemes/RudderSDK-iOS.xcscheme b/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-iOS.xcscheme similarity index 77% rename from Rudder.xcodeproj/xcshareddata/xcschemes/RudderSDK-iOS.xcscheme rename to Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-iOS.xcscheme index 3efd83d0..59efbf20 100644 --- a/Rudder.xcodeproj/xcshareddata/xcschemes/RudderSDK-iOS.xcscheme +++ b/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-iOS.xcscheme @@ -1,7 +1,7 @@ + LastUpgradeVersion = "1500" + version = "1.7"> @@ -16,7 +16,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "06CABB832630C3CA0097BEFF" BuildableName = "Rudder.framework" - BlueprintName = "Rudder" + BlueprintName = "Rudder-iOS" ReferencedContainer = "container:Rudder.xcodeproj"> @@ -26,19 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> diff --git a/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-macOS.xcscheme b/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-macOS.xcscheme new file mode 100644 index 00000000..b688f2e8 --- /dev/null +++ b/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-macOS.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-tvOS.xcscheme b/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-tvOS.xcscheme new file mode 100644 index 00000000..f3c673c3 --- /dev/null +++ b/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-tvOS.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-watchOS.xcscheme b/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-watchOS.xcscheme new file mode 100644 index 00000000..47727f17 --- /dev/null +++ b/Rudder.xcodeproj/xcshareddata/xcschemes/Rudder-watchOS.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests.xcscheme b/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-iOS.xcscheme similarity index 94% rename from Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests.xcscheme rename to Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-iOS.xcscheme index 1d9c9646..d72112df 100644 --- a/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests.xcscheme +++ b/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-iOS.xcscheme @@ -17,8 +17,8 @@ diff --git a/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-macOS.xcscheme b/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-macOS.xcscheme new file mode 100644 index 00000000..0d1b9c9c --- /dev/null +++ b/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-macOS.xcscheme @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-tvOS.xcscheme b/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-tvOS.xcscheme new file mode 100644 index 00000000..91373d65 --- /dev/null +++ b/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-tvOS.xcscheme @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-watchOS.xcscheme b/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-watchOS.xcscheme new file mode 100644 index 00000000..fd0ddc78 --- /dev/null +++ b/Rudder.xcodeproj/xcshareddata/xcschemes/RudderTests-watchOS.xcscheme @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/RudderTests/RSBlackListedEventsTest.swift b/RudderTests/RSBlackListedEventsTest.swift similarity index 78% rename from Tests/RudderTests/RSBlackListedEventsTest.swift rename to RudderTests/RSBlackListedEventsTest.swift index 91bcaa12..83ae25d4 100644 --- a/Tests/RudderTests/RSBlackListedEventsTest.swift +++ b/RudderTests/RSBlackListedEventsTest.swift @@ -9,14 +9,14 @@ import XCTest @testable import Rudder -// swiftlint:disable inclusive_language + class RSBlackListedEventsTest: XCTestCase { var client: RSClient! override func setUpWithError() throws { - client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) +// client = RSClient.sharedInstance() +// client.configure(with: RSConfig(writeKey: "WRITE_KEY").dataPlaneURL("DATA_PLANE_URL")) } override func tearDownWithError() throws { @@ -26,7 +26,7 @@ class RSBlackListedEventsTest: XCTestCase { // swiftlint:disable inclusive_language // make sure you select 'Blacklist' for 'Client-side Events Filtering' section in // Configuration from RudderStack dashboard. It will take 5 min to be affected. - func testBlackListedSuccess() { + /*func testBlackListedSuccess() { let expectation = XCTestExpectation(description: "Firebase Expectation") let myDestination = FirebaseDestination { expectation.fulfill() @@ -54,13 +54,5 @@ class RSBlackListedEventsTest: XCTestCase { waitUntilStarted(client: client) client.track("track_blacklist_2") wait(for: [expectation], timeout: 2.0) - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - + }*/ } diff --git a/RudderTests/RSClientTests.swift b/RudderTests/RSClientTests.swift new file mode 100644 index 00000000..1e273986 --- /dev/null +++ b/RudderTests/RSClientTests.swift @@ -0,0 +1,435 @@ +// +// RSClientTests.swift +// RudderStackTests +// +// Created by Pallab Maiti on 07/03/22. +// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import XCTest +@testable import Rudder + +class RSClientTests: XCTestCase { + + var client: RSClient! + + override func setUpWithError() throws { + try super.setUpWithError() + client = RSClient.sharedInstance() + let userDefaults = UserDefaults(suiteName: #file) ?? UserDefaults.standard + userDefaults.removePersistentDomain(forName: #file) + client.userDefaults = RSUserDefaults(userDefaults: userDefaults) + + client.configure(with: RSConfig(writeKey: "WRITE_KEY").dataPlaneURL("DATA_PLANE_URL")) + } + + override func tearDownWithError() throws { + try super.tearDownWithError() + client = nil + } + + func testAlias() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.alias("user_id") + + let aliasEvent1 = resultPlugin.lastMessage as? AliasMessage + + XCTAssertTrue(aliasEvent1?.userId == "user_id") + XCTAssertTrue(aliasEvent1?.type == .alias) + XCTAssertNil(aliasEvent1?.option) + XCTAssertNil(aliasEvent1?.previousId) + + client.alias("new_user_id") + + let aliasEvent2 = resultPlugin.lastMessage as? AliasMessage + + XCTAssertTrue(aliasEvent2?.userId == "new_user_id") + XCTAssertTrue(aliasEvent2?.previousId == "user_id") + XCTAssertTrue(aliasEvent2?.type == .alias) + XCTAssertNil(aliasEvent2?.option) + } + + func testGroup() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.group("sample_group_id") + + let groupEvent = resultPlugin.lastMessage as? GroupMessage + + XCTAssertTrue(groupEvent?.groupId == "sample_group_id") + XCTAssertTrue(groupEvent?.type == .group) + XCTAssertNil(groupEvent?.traits) + XCTAssertNil(groupEvent?.option) + } + + func testGroupWithTraits() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.group("sample_group_id", traits: ["key_1": "value_1", "key_2": "value_2"]) + + let groupEvent = resultPlugin.lastMessage as? GroupMessage + + XCTAssertTrue(groupEvent?.groupId == "sample_group_id") + XCTAssertTrue(groupEvent?.type == .group) + XCTAssertNotNil(groupEvent?.traits) + XCTAssertNil(groupEvent?.option) + + let traits = groupEvent?.traits + + XCTAssertTrue(traits?["key_1"] == "value_1") + XCTAssertTrue(traits?["key_2"] == "value_2") + } + + func testIdentify() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.identify("user_id") + + let identifyEvent = resultPlugin.lastMessage as? IdentifyMessage + + XCTAssertTrue(identifyEvent?.userId == "user_id") + XCTAssertTrue(identifyEvent?.type == .identify) + } + + func testIdentifyWithTraits() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.identify("user_id", traits: ["email": "abc@def.com"]) + + let identifyEvent = resultPlugin.lastMessage as? IdentifyMessage + + XCTAssertTrue(identifyEvent?.userId == "user_id") + XCTAssertTrue(identifyEvent?.type == .identify) + + let traits = identifyEvent?.traits + + XCTAssertTrue(traits?["email"] as? String == "abc@def.com") + XCTAssertFalse(traits?["name"] as? String == "name") + } + + func testUserIdAndTraitsPersistCorrectly() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.identify("user_id", traits: ["email": "abc@def.com"]) + + let identifyEvent = resultPlugin.lastMessage as? IdentifyMessage + + XCTAssertTrue(identifyEvent?.userId == "user_id") + XCTAssertTrue(identifyEvent?.type == .identify) + + let traits = identifyEvent?.traits + + XCTAssertTrue(traits?["email"] as? String == "abc@def.com") + XCTAssertFalse(traits?["name"] as? String == "name") + + client.track("simple_track") + + let trackEvent = resultPlugin.lastMessage as? TrackMessage + + XCTAssertTrue(trackEvent?.userId == "user_id") + let trackTraits = trackEvent?.context?["traits"] as? [String: Any] + XCTAssertNotNil(trackTraits) + XCTAssertTrue(trackTraits?["email"] as? String == "abc@def.com") + XCTAssertTrue(trackTraits?["userId"] as? String == "user_id") + } + + func testScreen() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.screen("ViewController") + + let screenEvent = resultPlugin.lastMessage as? ScreenMessage + XCTAssertTrue(screenEvent?.name == "ViewController") + XCTAssertTrue(screenEvent?.type == .screen) + } + + func testScreenWithProperties() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.screen("ViewController", properties: ["key_1": "value_1", "key_2": "value_2"]) + + let screenEvent = resultPlugin.lastMessage as? ScreenMessage + + XCTAssertTrue(screenEvent?.name == "ViewController") + XCTAssertTrue(screenEvent?.type == .screen) + XCTAssertNotNil(screenEvent?.properties) + XCTAssertNil(screenEvent?.option) + + let properties = screenEvent?.properties + + XCTAssertTrue(properties?["key_1"] as? String == "value_1") + XCTAssertTrue(properties?["key_2"] as? String == "value_2") + } + + func testTrack() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.track("simple_track") + + let trackEvent = resultPlugin.lastMessage as? TrackMessage + + XCTAssertTrue(trackEvent?.event == "simple_track") + XCTAssertTrue(trackEvent?.type == .track) + XCTAssertNil(trackEvent?.properties) + XCTAssertNil(trackEvent?.option) + } + + func testTrackWithProperties() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + client.track("simple_track_with_props", properties: ["key_1": "value_1", "key_2": "value_2"]) + + let trackEvent = resultPlugin.lastMessage as? TrackMessage + + XCTAssertTrue(trackEvent?.event == "simple_track_with_props") + XCTAssertTrue(trackEvent?.type == .track) + XCTAssertNotNil(trackEvent?.properties) + XCTAssertNil(trackEvent?.option) + + let properties = trackEvent?.properties + + XCTAssertTrue(properties?["key_1"] as? String == "value_1") + XCTAssertTrue(properties?["key_2"] as? String == "value_2") + } + + // make sure you have Firebase added & enabled to the source in your RudderStack A/C + /*func testDestinationEnabled() { + let expectation = XCTestExpectation(description: "Firebase Expectation") + let myDestination = FirebaseDestination { + expectation.fulfill() + return true + } + + client.addDestination(myDestination) + + waitUntilStarted(client: client) + + client.track("testDestinationEnabled") + + wait(for: [expectation], timeout: 2.0) + } + + func testDestinationNotEnabled() { + let expectation = XCTestExpectation(description: "MyDestination Expectation") + let myDestination = MyDestination { + expectation.fulfill() + return true + } + + client.addDestination(myDestination) + + waitUntilStarted(client: client) + client.track("testDestinationEnabled") + + XCTExpectFailure { + wait(for: [expectation], timeout: 2.0) + } + }*/ + + func testAnonymousId() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + client.setAnonymousId("anonymous_id") + + waitUntilStarted(client: client) + + client.track("test_anonymous_id") + + let anonId = client.anonymousId + + XCTAssertTrue(anonId != "") + XCTAssertTrue(anonId == "anonymous_id") + + let anonymousId = resultPlugin.lastMessage?.anonymousId + + XCTAssertTrue(anonymousId != "") + XCTAssertTrue(anonymousId == "anonymous_id") + } + + func testContext() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + + client.track("context check") + + let context = resultPlugin.lastMessage?.context + XCTAssertNotNil(context) + XCTAssertNotNil(context?["screen"], "screen missing!") + XCTAssertNotNil(context?["network"], "network missing!") + XCTAssertNotNil(context?["os"], "os missing!") + XCTAssertNotNil(context?["timezone"], "timezone missing!") + XCTAssertNotNil(context?["library"], "library missing!") + XCTAssertNotNil(context?["device"], "device missing!") + XCTAssertNotNil(context?["app"], "app missing!") + XCTAssertNotNil(context?["locale"], "locale missing!") + } + + func testDeviceToken() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + + client.setDeviceToken("device_token") + client.track("device token check") + + let context = resultPlugin.lastMessage?.context + let token = context?[keyPath: "device.token"] as? String + + XCTAssertTrue(token != "") + XCTAssertTrue(token == "device_token") + } + + func testContextTraits() { + let resultPlugin = ResultPlugin() + client.add(plugin: resultPlugin) + + waitUntilStarted(client: client) + + + client.identify("user_id", traits: ["email": "abc@def.com"]) + + let identifyEvent = resultPlugin.lastMessage as? IdentifyMessage + XCTAssertTrue(identifyEvent?.userId == "user_id") + let identifyTraits = identifyEvent?.traits + XCTAssertTrue(identifyTraits?["email"] as? String == "abc@def.com") + + client.track("test context") + + let trackEvent = resultPlugin.lastMessage as? TrackMessage + XCTAssertTrue(trackEvent?.userId == "user_id") + let trackTraits = trackEvent?.context?["traits"] as? [String: Any] + XCTAssertNotNil(trackTraits) + XCTAssertTrue(trackTraits?["email"] as? String == "abc@def.com") + XCTAssertTrue(trackTraits?["userId"] as? String == "user_id") + + let clientTraits = client.traits + XCTAssertNotNil(clientTraits) + XCTAssertTrue(clientTraits?["email"] as? String == "abc@def.com") + XCTAssertTrue(clientTraits?["userId"] as? String == "user_id") + } +} + +func waitUntilStarted(client: RSClient?) { + guard let client = client else { return } + if let replayQueue = client.find(pluginType: RSReplayQueuePlugin.self) { + while replayQueue.running == true { + RunLoop.main.run(until: Date.distantPast) + } + } +} + +class FirebaseDestinationPlugin: RSDestinationPlugin { + var controller: RSController = RSController() + var client: RSClient? + var type: PluginType = .destination + var key: String = "Firebase" + + let trackCompletion: (() -> Bool)? + + init(trackCompletion: (() -> Bool)? = nil) { + self.trackCompletion = trackCompletion + } + + func track(message: TrackMessage) -> TrackMessage? { + var returnEvent: TrackMessage? = message + if let completion = trackCompletion { + if !completion() { + returnEvent = nil + } + } + return returnEvent + } +} + +class MyDestinationPlugin: RSDestinationPlugin { + var controller: RSController = RSController() + var client: RSClient? + var type: PluginType = .destination + var key: String = "MyDestination" + + let trackCompletion: (() -> Bool)? + + init(trackCompletion: (() -> Bool)? = nil) { + self.trackCompletion = trackCompletion + } + + func track(message: TrackMessage) -> TrackMessage? { + var returnEvent: TrackMessage? = message + if let completion = trackCompletion { + if !completion() { + returnEvent = nil + } + } + return returnEvent + } +} + +class FirebaseDestination: RudderDestination { + init(trackCompletion: (() -> Bool)?) { + super.init() + plugin = FirebaseDestinationPlugin(trackCompletion: trackCompletion) + } +} + +class MyDestination: RudderDestination { + init(trackCompletion: (() -> Bool)?) { + super.init() + plugin = MyDestinationPlugin(trackCompletion: trackCompletion) + } +} + +class ResultPlugin: RSPlugin { + let type: PluginType = .after + var client: RSClient? + var lastMessage: RSMessage? + var trackList = [TrackMessage]() + var identifyList = [IdentifyMessage]() + + func execute(message: T?) -> T? where T: RSMessage { + lastMessage = message + if let message = message as? TrackMessage { + trackList.append(message) + } + if let message = message as? IdentifyMessage { + identifyList.append(message) + } + return message + } +} diff --git a/RudderTests/RSClientTests.swift.plist b/RudderTests/RSClientTests.swift.plist new file mode 100644 index 00000000..09debe4a Binary files /dev/null and b/RudderTests/RSClientTests.swift.plist differ diff --git a/Tests/RudderTests/RSDatabaseTests.swift b/RudderTests/RSDatabaseTests.swift similarity index 87% rename from Tests/RudderTests/RSDatabaseTests.swift rename to RudderTests/RSDatabaseTests.swift index 18249cc5..3a8af547 100644 --- a/Tests/RudderTests/RSDatabaseTests.swift +++ b/RudderTests/RSDatabaseTests.swift @@ -16,18 +16,18 @@ class RSDatabaseTests: XCTestCase { var databaseManager: RSDatabaseManager! override func setUpWithError() throws { - client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) - databaseManager = RSDatabaseManager(client: client) +// client = RSClient.sharedInstance() +// client.configure(with: RSConfig(writeKey: "WRITE_KEY").dataPlaneURL("DATA_PLANE_URL")) +// databaseManager = RSDatabaseManager(client: client) } override func tearDownWithError() throws { client = nil } - func testWriteEvent() { + /*func testWriteEvent() { let trackMessage = TrackMessage(event: "sample_track1", properties: nil) - .applyRawEventData() + .applyRawEventData(userInfo: client.userInfo) databaseManager.write(trackMessage) RunLoop.current.run(until: Date(timeIntervalSinceNow: 2)) @@ -42,7 +42,7 @@ class RSDatabaseTests: XCTestCase { func testRemoveEvent() { let trackMessage = TrackMessage(event: "sample_track1", properties: nil) - .applyRawEventData() + .applyRawEventData(userInfo: client.userInfo) databaseManager.write(trackMessage) RunLoop.current.run(until: Date(timeIntervalSinceNow: 2)) @@ -64,7 +64,7 @@ class RSDatabaseTests: XCTestCase { } else { XCTFail("No events found") } - } + }*/ func fetchAllEvents() -> [[String: Any]]? { let totalCount = databaseManager.getDBRecordCount() diff --git a/Tests/RudderTests/RSThreadTests.swift b/RudderTests/RSThreadTests.swift similarity index 98% rename from Tests/RudderTests/RSThreadTests.swift rename to RudderTests/RSThreadTests.swift index 8da2d206..e65ad7da 100644 --- a/Tests/RudderTests/RSThreadTests.swift +++ b/RudderTests/RSThreadTests.swift @@ -11,7 +11,7 @@ import XCTest class RSThreadTests: XCTestCase { - var client: RSClient! + /*var client: RSClient! override func setUpWithError() throws { client = RSClient.sharedInstance() @@ -89,5 +89,5 @@ class RSThreadTests: XCTestCase { } wait(for: [exp], timeout: 100) - } + }*/ } diff --git a/Tests/RudderTests/RSWhiteListedEventsTests.swift b/RudderTests/RSWhiteListedEventsTests.swift similarity index 92% rename from Tests/RudderTests/RSWhiteListedEventsTests.swift rename to RudderTests/RSWhiteListedEventsTests.swift index 93d8291a..176ff160 100644 --- a/Tests/RudderTests/RSWhiteListedEventsTests.swift +++ b/RudderTests/RSWhiteListedEventsTests.swift @@ -12,11 +12,11 @@ import XCTest // swiftlint:disable inclusive_language class RSWhiteListedEventsTests: XCTestCase { - var client: RSClient! + /*var client: RSClient! override func setUpWithError() throws { client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) + client.configure(with: RSConfig(writeKey: "WRITE_KEY").dataPlaneURL("DATA_PLANE_URL")) } override func tearDownWithError() throws { @@ -54,5 +54,5 @@ class RSWhiteListedEventsTests: XCTestCase { XCTExpectFailure { wait(for: [expectation], timeout: 2.0) } - } + }*/ } diff --git a/Tests/RudderTests/RSiOSLifeCycleEventTests.swift b/RudderTests/RSiOSLifeCycleEventTests.swift similarity index 86% rename from Tests/RudderTests/RSiOSLifeCycleEventTests.swift rename to RudderTests/RSiOSLifeCycleEventTests.swift index 4e9aa525..13b4aade 100644 --- a/Tests/RudderTests/RSiOSLifeCycleEventTests.swift +++ b/RudderTests/RSiOSLifeCycleEventTests.swift @@ -16,7 +16,7 @@ class RSiOSLifeCycleEventTests: XCTestCase { override func setUpWithError() throws { client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) + client.configure(with: RSConfig(writeKey: "WRITE_KEY").dataPlaneURL("DATA_PLANE_URL")) } override func tearDownWithError() throws { @@ -31,10 +31,10 @@ class RSiOSLifeCycleEventTests: XCTestCase { client.add(plugin: iOSLifeCyclePlugin) waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - RSUserDefaults.saveApplicationVersion(nil) - RSUserDefaults.saveApplicationBuild(nil) + + client.userDefaults.write(application: .version, value: nil) + client.userDefaults.write(application: .build, value: nil) // This is a hack that needs to be dealt with RunLoop.current.run(until: Date(timeIntervalSinceNow: 2)) @@ -57,10 +57,10 @@ class RSiOSLifeCycleEventTests: XCTestCase { client.add(plugin: iOSLifeCyclePlugin) waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - RSUserDefaults.saveApplicationVersion("2.0.0") - RSUserDefaults.saveApplicationBuild("2") + + client.userDefaults.write(application: .version, value: "2.0.0") + client.userDefaults.write(application: .build, value: "2") // This is a hack that needs to be dealt with RunLoop.current.run(until: Date(timeIntervalSinceNow: 2)) @@ -83,7 +83,7 @@ class RSiOSLifeCycleEventTests: XCTestCase { client.add(plugin: iOSLifeCyclePlugin) waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) + iOSLifeCyclePlugin.application(nil, didFinishLaunchingWithOptions: nil) @@ -100,7 +100,7 @@ class RSiOSLifeCycleEventTests: XCTestCase { client.add(plugin: iOSLifeCyclePlugin) waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) + iOSLifeCyclePlugin.applicationDidEnterBackground(application: nil) diff --git a/Tests/RudderTests/RSmacOSLifeCycleEventTests.swift b/RudderTests/RSmacOSLifeCycleEventTests.swift similarity index 90% rename from Tests/RudderTests/RSmacOSLifeCycleEventTests.swift rename to RudderTests/RSmacOSLifeCycleEventTests.swift index c936e7ef..8703d204 100644 --- a/Tests/RudderTests/RSmacOSLifeCycleEventTests.swift +++ b/RudderTests/RSmacOSLifeCycleEventTests.swift @@ -16,7 +16,7 @@ class RSmacOSLifeCycleEventTests: XCTestCase { override func setUpWithError() throws { client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) + client.configure(with: RSConfig(writeKey: "WRITE_KEY").dataPlaneURL("DATA_PLANE_URL")) } override func tearDownWithError() throws { @@ -33,8 +33,8 @@ class RSmacOSLifeCycleEventTests: XCTestCase { waitUntilStarted(client: client) waitUntilServerConfigDownloaded(client: client) - RSUserDefaults.saveApplicationVersion(nil) - RSUserDefaults.saveApplicationBuild(nil) + client.userDefaults.write(application: .version, value: nil) + client.userDefaults.write(application: .build, value: nil) // This is a hack that needs to be dealt with RunLoop.current.run(until: Date(timeIntervalSinceNow: 2)) @@ -59,8 +59,8 @@ class RSmacOSLifeCycleEventTests: XCTestCase { waitUntilStarted(client: client) waitUntilServerConfigDownloaded(client: client) - RSUserDefaults.saveApplicationVersion("2.0.0") - RSUserDefaults.saveApplicationBuild("2") + client.userDefaults.write(application: .version, value: "2.0.0") + client.userDefaults.write(application: .build, value: "2") // This is a hack that needs to be dealt with RunLoop.current.run(until: Date(timeIntervalSinceNow: 2)) diff --git a/Tests/RudderTests/RSwatchOSLifeCycleEventTests.swift b/RudderTests/RSwatchOSLifeCycleEventTests.swift similarity index 84% rename from Tests/RudderTests/RSwatchOSLifeCycleEventTests.swift rename to RudderTests/RSwatchOSLifeCycleEventTests.swift index d3174e45..30fb0ad2 100644 --- a/Tests/RudderTests/RSwatchOSLifeCycleEventTests.swift +++ b/RudderTests/RSwatchOSLifeCycleEventTests.swift @@ -16,7 +16,7 @@ class RSwatchOSLifeCycleEventTests: XCTestCase { override func setUpWithError() throws { client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) + client.configure(with: RSConfig(writeKey: "WRITE_KEY").dataPlaneURL("DATA_PLANE_URL")) } override func tearDownWithError() throws { @@ -31,11 +31,10 @@ class RSwatchOSLifeCycleEventTests: XCTestCase { client.add(plugin: watchOSLifeCyclePlugin) waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - RSUserDefaults.saveApplicationVersion(nil) - RSUserDefaults.saveApplicationBuild(nil) - + client.userDefaults.write(application: .version, value: nil) + client.userDefaults.write(application: .build, value: nil) + // This is a hack that needs to be dealt with RunLoop.current.run(until: Date(timeIntervalSinceNow: 2)) @@ -57,11 +56,10 @@ class RSwatchOSLifeCycleEventTests: XCTestCase { client.add(plugin: watchOSLifeCyclePlugin) waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - RSUserDefaults.saveApplicationVersion("2.0.0") - RSUserDefaults.saveApplicationBuild("2") + client.userDefaults.write(application: .version, value: "2.0.0") + client.userDefaults.write(application: .build, value: "2") + // This is a hack that needs to be dealt with RunLoop.current.run(until: Date(timeIntervalSinceNow: 2)) @@ -83,7 +81,6 @@ class RSwatchOSLifeCycleEventTests: XCTestCase { client.add(plugin: watchOSLifeCyclePlugin) waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) watchOSLifeCyclePlugin.applicationWillEnterForeground(watchExtension: nil) diff --git a/Sources/Classes/Client/Plugins/RSAdvertisementIdPlugin.swift b/Sources/Classes/Client/Plugins/RSAdvertisementIdPlugin.swift deleted file mode 100644 index df1e476e..00000000 --- a/Sources/Classes/Client/Plugins/RSAdvertisementIdPlugin.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// RSAdvertisementIdPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 02/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSAdvertisingIdPlugin: RSPlatformPlugin { - let type = PluginType.before - var client: RSClient? - - var advertisingId: String? - - required init() { } - - func execute(message: T?) -> T? { - guard var workingMessage = message else { return message } - if var context = workingMessage.context, let advertisingId = advertisingId { - context[keyPath: "device.advertisingId"] = advertisingId - context[keyPath: "device.adTrackingEnabled"] = true - workingMessage.context = context - client?.updateContext(context) - } - return workingMessage - } -} - -extension RSClient { - /** - API for setting identifier under context.device.advertisingId. - - Parameters: - - advertisingId: IDFA value - # Example # - ``` - client.setAdvertisingId("sample_device_token") - ``` - */ - @objc - public func setAdvertisingId(_ advertisingId: String) { - guard advertisingId.isNotEmpty else { - log(message: "advertisingId can not be empty", logLevel: .warning) - return - } - if let advertisingIdPlugin = self.find(pluginType: RSAdvertisingIdPlugin.self) { - advertisingIdPlugin.advertisingId = advertisingId - } else { - let advertisingIdPlugin = RSAdvertisingIdPlugin() - advertisingIdPlugin.advertisingId = advertisingId - add(plugin: advertisingIdPlugin) - } - } -} diff --git a/Sources/Classes/Client/Plugins/RSAliasIdPlugin.swift b/Sources/Classes/Client/Plugins/RSAliasIdPlugin.swift deleted file mode 100644 index a38f75a3..00000000 --- a/Sources/Classes/Client/Plugins/RSAliasIdPlugin.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// RSAliasIdPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 31/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSAliasIdPlugin: RSPlatformPlugin { - let type = PluginType.before - var client: RSClient? - - var id: String? - - required init() { } - - func execute(message: T?) -> T? { - guard var workingMessage = message else { return message } - if let id = id { - if var context = workingMessage.context { - context[keyPath: "traits.id"] = id - workingMessage.context = context - client?.updateContext(context) - } - } - return workingMessage - } -} - -extension RSAliasIdPlugin: RSEventPlugin { - func reset() { - id = nil - } -} - -extension RSClient { - internal func setAlias(_ id: String) { - if let aliasIdPlugin = self.find(pluginType: RSAliasIdPlugin.self) { - aliasIdPlugin.id = id - } else { - let aliasIdPlugin = RSAliasIdPlugin() - aliasIdPlugin.id = id - add(plugin: aliasIdPlugin) - } - } -} diff --git a/Sources/Classes/Client/Plugins/RSAnonymousIdPlugin.swift b/Sources/Classes/Client/Plugins/RSAnonymousIdPlugin.swift deleted file mode 100644 index 51247ae0..00000000 --- a/Sources/Classes/Client/Plugins/RSAnonymousIdPlugin.swift +++ /dev/null @@ -1,126 +0,0 @@ -// -// RSAnonymousIdPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 02/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -// MARK: - iOS, tvOS, Catalyst - -#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) -import SystemConfiguration -import UIKit -#if !os(tvOS) -import WebKit -#endif -#endif - -// MARK: - watchOS - -#if os(watchOS) -import WatchKit -import Network -#endif - -// MARK: - macOS - -#if os(macOS) -import Cocoa -import WebKit -#endif - -class RSAnonymousIdPlugin: RSPlatformPlugin { - let type = PluginType.before - var client: RSClient? - - var anonymousId: String? - - required init() { -#if os(iOS) || os(tvOS) - anonymousId = UIDevice.current.identifierForVendor?.uuidString.lowercased() -#endif -#if os(watchOS) - anonymousId = WKInterfaceDevice.current().identifierForVendor?.uuidString.lowercased() -#endif -#if os(macOS) - anonymousId = macAddress(bsd: "en0") -#endif - } - - private func macAddress(bsd: String) -> String? { - let MAC_ADDRESS_LENGTH = 6 - let separator = ":" - - var length: size_t = 0 - var buffer: [CChar] - - let bsdIndex = Int32(if_nametoindex(bsd)) - if bsdIndex == 0 { - return nil - } - let bsdData = Data(bsd.utf8) - var managementInfoBase = [CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, bsdIndex] - - if sysctl(&managementInfoBase, 6, nil, &length, nil, 0) < 0 { - return nil - } - - buffer = [CChar](unsafeUninitializedCapacity: length, initializingWith: {buffer, initializedCount in - for x in 0...stride + 1 - let rangeOfToken = infoData[indexAfterMsghdr...].range(of: bsdData)! - let lower = rangeOfToken.upperBound - let upper = lower + MAC_ADDRESS_LENGTH - let macAddressData = infoData[lower..(message: T?) -> T? { - guard var workingMessage = message else { return message } - workingMessage.anonymousId = anonymousId - if var context = workingMessage.context { - context[keyPath: "traits.anonymousId"] = anonymousId - workingMessage.context = context - client?.updateContext(context) - } - return workingMessage - } -} - -extension RSClient { - /** - API for setting unique identifier of every call. - - Parameters: - - anonymousId: Unique identifier of every event - # Example # - ``` - client.setAnonymousId("sample_anonymous_id") - ``` - */ - @objc - public func setAnonymousId(_ anonymousId: String) { - guard anonymousId.isNotEmpty else { - log(message: "anonymousId can not be empty", logLevel: .warning) - return - } - if let anonymousIdPlugin = self.find(pluginType: RSAnonymousIdPlugin.self) { - anonymousIdPlugin.anonymousId = anonymousId - } else { - let anonymousIdPlugin = RSAnonymousIdPlugin() - anonymousIdPlugin.anonymousId = anonymousId - add(plugin: anonymousIdPlugin) - } - } -} diff --git a/Sources/Classes/Client/Plugins/RSAppTrackingConsentPlugin.swift b/Sources/Classes/Client/Plugins/RSAppTrackingConsentPlugin.swift deleted file mode 100644 index c94e2712..00000000 --- a/Sources/Classes/Client/Plugins/RSAppTrackingConsentPlugin.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// RSAppTrackingConsentPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 02/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSAppTrackingConsentPlugin: RSPlatformPlugin { - let type = PluginType.before - var client: RSClient? - - var appTrackingConsent: RSAppTrackingConsent? - - required init() { } - - func execute(message: T?) -> T? { - guard var workingMessage = message else { return message } - if var context = workingMessage.context, let appTrackingConsent = appTrackingConsent { - context[keyPath: "device.attTrackingStatus"] = appTrackingConsent.rawValue - workingMessage.context = context - client?.updateContext(context) - } - return workingMessage - } -} - -extension RSClient { - /** - API for app tracking consent management. - - Parameters: - - appTrackingConsent: App tracking consent - # Example # - ``` - client.setAppTrackingConsent(.authorize) - ``` - */ - @objc - public func setAppTrackingConsent(_ appTrackingConsent: RSAppTrackingConsent) { - if let appTrackingConsentPlugin = self.find(pluginType: RSAppTrackingConsentPlugin.self) { - appTrackingConsentPlugin.appTrackingConsent = appTrackingConsent - } else { - let appTrackingConsentPlugin = RSAppTrackingConsentPlugin() - appTrackingConsentPlugin.appTrackingConsent = appTrackingConsent - add(plugin: appTrackingConsentPlugin) - } - } -} diff --git a/Sources/Classes/Client/Plugins/RSContextPlugin.swift b/Sources/Classes/Client/Plugins/RSContextPlugin.swift index 11f8a2b4..0e6711a4 100644 --- a/Sources/Classes/Client/Plugins/RSContextPlugin.swift +++ b/Sources/Classes/Client/Plugins/RSContextPlugin.swift @@ -10,118 +10,88 @@ import Foundation class RSContextPlugin: RSPlatformPlugin { let type: PluginType = .before - var client: RSClient? - - var context: MessageContext? - - var traits: MessageTraits? { - if let traits = context?["traits"] as? MessageTraits { - return traits + weak var client: RSClient? { + didSet { + initialSetup() } - return nil } private var staticContext = staticContextData() private static var device = Vendor.current + private var userDefaults: RSUserDefaults? + + func initialSetup() { + guard let client = self.client else { return } + userDefaults = client.userDefaults + } func execute(message: T?) -> T? { - guard var workingMessage = message else { return message } + guard var workingMessage = message else { return message } var context = staticContext insertDynamicPlatformContextData(context: &context) insertDynamicOptionData(message: workingMessage, context: &context) + insertDynamicDeviceInfoData(context: &context) + insertSessionData(message: workingMessage, context: &context) + if let eventContext = workingMessage.context { + context.merge(eventContext) { (new, _) in new } + } workingMessage.context = context - self.context = context + RSSessionStorage.shared.write(.context, value: context) return workingMessage } internal static func staticContextData() -> [String: Any] { var staticContext = [String: Any]() - // library name - staticContext["library"] = [ - "name": "rudder-ios-library", - "version": RSVersion - ] - + staticContext["library"] = RSContext.LibraryInfo().dictionary // app info - let info = Bundle.main.infoDictionary - staticContext["app"] = [ - "name": info?["CFBundleDisplayName"] ?? "", - "version": info?["CFBundleShortVersionString"] ?? "", - "build": info?["CFBundleVersion"] ?? "", - "namespace": Bundle.main.bundleIdentifier ?? "" - ] + staticContext["app"] = RSContext.AppInfo().dictionary insertStaticPlatformContextData(context: &staticContext) return staticContext } internal static func insertStaticPlatformContextData(context: inout [String: Any]) { // device - let device = Self.device - - let deviceInfo = [ - "manufacturer": device.manufacturer, - "type": device.type, - "model": device.model, - "name": device.name, - "id": device.identifierForVendor ?? "" - ] - context["device"] = deviceInfo + context["device"] = RSContext.DeviceInfo().dictionary // os - context["os"] = [ - "name": device.systemName, - "version": device.systemVersion - ] + context["os"] = RSContext.OSInfo().dictionary // screen - let screen = device.screenSize - context["screen"] = [ - "width": screen.width, - "height": screen.height, - "density": screen.density - ] + context["screen"] = RSContext.ScreenInfo().dictionary // locale - if !Locale.preferredLanguages.isEmpty { - context["locale"] = Locale.preferredLanguages[0] - } + context["locale"] = RSContext.locale() // timezone - context["timezone"] = TimeZone.current.identifier + context["timezone"] = RSContext.timezone() } internal func insertDynamicPlatformContextData(context: inout [String: Any]) { - let device = Self.device - - // network - let status = device.connection - - var cellular = false - var wifi = false - var bluetooth = false - - switch status { - case .online(.cellular): - cellular = true - case .online(.wifi): - wifi = true - case .online(.bluetooth): - bluetooth = true - default: - break - } - // network connectivity - context["network"] = [ - "bluetooth": bluetooth, - "cellular": cellular, - "wifi": wifi, - "carrier": device.carrier - ] as [String: Any] + context["network"] = RSContext.NetworkInfo().dictionary + } + + internal func insertDynamicDeviceInfoData(context: inout [String: Any]) { + if let deviceToken: String = RSSessionStorage.shared.read(.deviceToken) { + context[keyPath: "device.token"] = deviceToken + } + if let advertisingId: String = RSSessionStorage.shared.read(.advertisingId), advertisingId.isNotEmpty { + context[keyPath: "device.advertisingId"] = advertisingId + context[keyPath: "device.adTrackingEnabled"] = true + let appTrackingConsent: RSAppTrackingConsent = RSSessionStorage.shared.read(.appTrackingConsent) ?? .notDetermined + context[keyPath: "device.attTrackingStatus"] = appTrackingConsent.rawValue + } } func insertDynamicOptionData(message: RSMessage, context: inout [String: Any]) { // First priority will given to the `option` passed along with the event + var contextExternalIds = [[String: String]]() + // Fetch `externalIds` set using identify API. + if let externalIds: [[String: String]] = userDefaults?.read(.externalId) { + contextExternalIds.append(contentsOf: externalIds) + } + if let option = message.option { - if let externalIds = option.externalIds { - context["externalId"] = externalIds + // We will merge the external ids for other event calls + if let externalIds = option.externalIds, message.type != .identify { + contextExternalIds.append(contentsOf: externalIds) } if let customContexts = option.customContexts { for (key, value) in customContexts { @@ -129,14 +99,17 @@ class RSContextPlugin: RSPlatformPlugin { } } } - // TODO: Fetch `customContexts` set using setOption API. + if !contextExternalIds.isEmpty { + context["externalId"] = contextExternalIds + } } -} - -extension RSClient { - func updateContext(_ context: MessageContext?) { - if let contextPlugin = self.find(pluginType: RSContextPlugin.self) { - contextPlugin.context = context + + func insertSessionData(message: RSMessage, context: inout [String: Any]) { + if let sessionId = message.sessionId { + context["sessionId"] = sessionId + if let sessionStart = message.sessionStart, sessionStart { + context["sessionStart"] = sessionStart + } } } } diff --git a/Sources/Classes/Client/Plugins/RSDeviceTokenPlugin.swift b/Sources/Classes/Client/Plugins/RSDeviceTokenPlugin.swift deleted file mode 100644 index d2bac152..00000000 --- a/Sources/Classes/Client/Plugins/RSDeviceTokenPlugin.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// RSDeviceTokenPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 24/02/22. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSDeviceTokenPlugin: RSPlatformPlugin { - let type = PluginType.before - var client: RSClient? - - var token: String? - - required init() { } - - func execute(message: T?) -> T? { - guard var workingMessage = message else { return message } - if var context = workingMessage.context, let token = token { - context[keyPath: "device.token"] = token - workingMessage.context = context - client?.updateContext(context) - } - return workingMessage - } -} - -extension RSClient { - /** - API for setting token under context.device.token. - - Parameters: - - token: Token of the device - # Example # - ``` - client.setDeviceToken("sample_device_token") - ``` - */ - @objc - public func setDeviceToken(_ token: String) { - guard token.isNotEmpty else { - log(message: "token can not be empty", logLevel: .warning) - return - } - if let tokenPlugin = self.find(pluginType: RSDeviceTokenPlugin.self) { - tokenPlugin.token = token - } else { - let tokenPlugin = RSDeviceTokenPlugin() - tokenPlugin.token = token - add(plugin: tokenPlugin) - } - } -} - -extension Data { - var hexString: String { - let hexString = map { String(format: "%02.2hhx", $0) }.joined() - return hexString - } -} diff --git a/Sources/Classes/Client/Plugins/RSGDPRPlugin.swift b/Sources/Classes/Client/Plugins/RSGDPRPlugin.swift deleted file mode 100644 index 3cd74372..00000000 --- a/Sources/Classes/Client/Plugins/RSGDPRPlugin.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// RSGDPRPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 03/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSGDPRPlugin: RSPlatformPlugin { - let type = PluginType.before - var client: RSClient? - - var optOutStatus: Bool? - - required init() { } - - func execute(message: T?) -> T? { - if RSUserDefaults.getOptStatus() == true { - return nil - } else { - return message - } - } -} - -extension RSClient { - @objc - public func setOptOutStatus(_ status: Bool) { - RSUserDefaults.saveOptStatus(status) - if let gdprPlugin = self.find(pluginType: RSGDPRPlugin.self) { - gdprPlugin.optOutStatus = status - } else { - let gdprPlugin = RSGDPRPlugin() - gdprPlugin.optOutStatus = status - add(plugin: gdprPlugin) - } - } - - @objc - public func getOptOutStatus() -> Bool { - return RSUserDefaults.getOptStatus() ?? false - } -} diff --git a/Sources/Classes/Client/Plugins/RSIdentifyTraitsPlugin.swift b/Sources/Classes/Client/Plugins/RSIdentifyTraitsPlugin.swift deleted file mode 100644 index 1149dec8..00000000 --- a/Sources/Classes/Client/Plugins/RSIdentifyTraitsPlugin.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// RSIdentifyTraitsPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 31/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSIdentifyTraitsPlugin: RSPlatformPlugin { - let type = PluginType.before - var client: RSClient? { - didSet { - initialSetup() - } - } - - var traits: IdentifyTraits? - - required init() { } - - func initialSetup() { - if let data: Data = RSUserDefaults.getTraits() { - do { - if let dictionary = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [String: Any] { - traits = dictionary - } - } catch { - self.client?.log(message: "Failed to decode traits: \(error)", logLevel: .error) - } - } - } - - func execute(message: T?) -> T? { - guard var workingMessage = message else { return message } - if let traits = traits { - if var context = workingMessage.context { - context["traits"] = traits - workingMessage.context = context - client?.updateContext(context) - } - } - return workingMessage - } -} - -extension RSIdentifyTraitsPlugin: RSEventPlugin { - func reset() { - traits = nil - RSUserDefaults.saveTraits(nil) - } - - func saveTraits(_ traits: [String: Any]?) { - guard let traits = traits else { - RSUserDefaults.saveTraits(nil) - return - } - do { - let data: Data = try NSKeyedArchiver.archivedData(withRootObject: traits, requiringSecureCoding: false) - RSUserDefaults.saveTraits(data) - } catch { - self.client?.log(message: "Failed to encode traits: \(error)", logLevel: .error) - } - } -} - -extension RSClient { - internal func setTraits(_ traits: IdentifyTraits?, _ newUserId: String) { - if let traitsPlugin = self.find(pluginType: RSIdentifyTraitsPlugin.self) { - var updatedTraits = traits - if let exisitingUserId = RSUserDefaults.getUserId(), exisitingUserId == newUserId { - guard let newTraits = traits else { return } - if var existingTraits = traitsPlugin.traits { - existingTraits.merge(newTraits) { (_, new) in new } - updatedTraits = existingTraits - } - } - traitsPlugin.traits = updatedTraits - traitsPlugin.saveTraits(updatedTraits) - } else { - let traitsPlugin = RSIdentifyTraitsPlugin() - traitsPlugin.traits = traits - traitsPlugin.saveTraits(traits) - add(plugin: traitsPlugin) - } - } -} diff --git a/Sources/Classes/Client/Plugins/RSIntegrationPlugin.swift b/Sources/Classes/Client/Plugins/RSIntegrationPlugin.swift index eddb411a..65a81274 100644 --- a/Sources/Classes/Client/Plugins/RSIntegrationPlugin.swift +++ b/Sources/Classes/Client/Plugins/RSIntegrationPlugin.swift @@ -10,7 +10,7 @@ import Foundation class RSIntegrationPlugin: RSPlatformPlugin { let type: PluginType = .before - var client: RSClient? + weak var client: RSClient? func execute(message: T?) -> T? { guard var workingMessage = message else { return message } @@ -19,8 +19,8 @@ class RSIntegrationPlugin: RSPlatformPlugin { if messageIntegrations["All"] == nil { workingMessage.integrations?["All"] = true } - } else if let optionPlugin = client?.find(pluginType: RSOptionPlugin.self) { - if let integrations = optionPlugin.option?.integrations { + } else if let globalOption: RSOption = RSSessionStorage.shared.read(.option) { + if let integrations = globalOption.integrations { workingMessage.integrations = integrations if integrations["All"] == nil { workingMessage.integrations?["All"] = true diff --git a/Sources/Classes/Client/Plugins/RSLoggerPlugin.swift b/Sources/Classes/Client/Plugins/RSLoggerPlugin.swift deleted file mode 100644 index a0dfda2f..00000000 --- a/Sources/Classes/Client/Plugins/RSLoggerPlugin.swift +++ /dev/null @@ -1,155 +0,0 @@ -// -// RSLoggerPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 24/02/22. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -// MARK: - Plugin Implementation - -class RSLoggerPlugin: RSUtilityPlugin { - var logLevel = RSLogLevel.debug - - var client: RSClient? { - didSet { - addTargets() - } - } - - let type = PluginType.utility - - fileprivate var loggingMediator = [RSLoggingType: RSLogger]() - - // Default to no, enable to see local logs - static var loggingEnabled = false - - // For use only. Note: This will contain the last created instance - // of analytics when used in a multi-analytics environment. - static var sharedAnalytics: RSClient? - - #if DEBUG - static var globalLogger: RSLoggerPlugin { - let logger = RSLoggerPlugin() - logger.addTargets() - return logger - } - #endif - - required init() { } - - func configure(client: RSClient) { - self.client = client - RSLoggerPlugin.sharedAnalytics = client - addTargets() - } - - func addTargets() { - try? add(target: RSConsoleLogger(), for: RSLoggingType.log) - } - - func loggingEnabled(_ enabled: Bool) { - RSLoggerPlugin.loggingEnabled = enabled - } - - func log(_ logMessage: RSLogMessage, destination: RSLoggingType.LogDestination) { - if client?.config?.logLevel == .verbose || logMessage.logLevel == client?.config?.logLevel { - for (logType, target) in loggingMediator where logType.contains(destination) { - target.parseLog(logMessage) - } - } - } - - func add(target: RSLogger, for loggingType: RSLoggingType) throws { - - // Verify the target does not exist, if it does bail out - let filtered = loggingMediator.filter { (type: RSLoggingType, existingTarget: RSLogger) in - Swift.type(of: existingTarget) == Swift.type(of: target) - } - if filtered.isEmpty == false { throw NSError(domain: "Target already exists", code: 2002, userInfo: nil) } - - // Finally add the target - loggingMediator[loggingType] = target - } -} - -// MARK: - Types - -struct LogFactory { - static func buildLog(destination: RSLoggingType.LogDestination, - title: String, - message: String, - logLevel: RSLogLevel = .debug, - function: String? = nil, - line: Int? = nil, - event: RSMessage? = nil, - sender: Any? = nil, - value: Double? = nil, - tags: [String]? = nil) -> RSLogMessage { - - switch destination { - case .log: - return GenericLog(logLevel: logLevel, message: message, function: function, line: line) - case .metric: - return MetricLog(title: title, message: message, value: value ?? 1, event: event, function: function, line: line) - } - } - - fileprivate struct GenericLog: RSLogMessage { - var logLevel: RSLogLevel - var title: String? - var message: String - var event: RSMessage? - var function: String? - var line: Int? - var logType: RSLoggingType.LogDestination = .log - var dateTime = Date() - } - - fileprivate struct MetricLog: RSLogMessage { - var title: String? - var logLevel: RSLogLevel = .debug - var message: String - var value: Double - var event: RSMessage? - var function: String? - var line: Int? - var logType: RSLoggingType.LogDestination = .metric - var dateTime = Date() - } -} - -extension RSClient { - static func rsLog(message: String, logLevel: RSLogLevel? = nil, function: String = #function, line: Int = #line) { - if let shared = RSLoggerPlugin.sharedAnalytics { - shared.apply { plugin in - if let loggerPlugin = plugin as? RSLoggerPlugin { - var filterKind = loggerPlugin.logLevel - if let logKind = logLevel { - filterKind = logKind - } - - let log = LogFactory.buildLog(destination: .log, title: "", message: message, logLevel: filterKind, function: function, line: line) - loggerPlugin.log(log, destination: .log) - } - } - } else { - #if DEBUG - let log = LogFactory.buildLog(destination: .log, title: "", message: message, logLevel: .debug, function: function, line: line) - RSLoggerPlugin.globalLogger.log(log, destination: .log) - #endif - } - } - - static func rsMetric(_ type: RSMetricType, name: String, value: Double, tags: [String]? = nil) { - RSLoggerPlugin.sharedAnalytics?.apply { plugin in - - if let loggerPlugin = plugin as? RSLoggerPlugin { - let log = LogFactory.buildLog(destination: .metric, title: type.toString(), message: name, value: value, tags: tags) - loggerPlugin.log(log, destination: .metric) - } - } - } -} diff --git a/Sources/Classes/Client/Plugins/RSOptionPlugin.swift b/Sources/Classes/Client/Plugins/RSOptionPlugin.swift deleted file mode 100644 index fb418cd8..00000000 --- a/Sources/Classes/Client/Plugins/RSOptionPlugin.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// RSOptionPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 02/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSOptionPlugin: RSPlatformPlugin { - let type: PluginType = .before - var client: RSClient? - - var option: RSOption? - - required init() { } -} - -extension RSClient { - /** - API for setting enable/disable sending the events across all the event calls made using the SDK to the specified destinations. - - Parameters: - - option: Options related to every API call - # Example # - ``` - let defaultOption = RSOption() - defaultOption.putIntegration("Amplitude", isEnabled: true) - - client.setOption(defaultOption) - ``` - */ - @objc - public func setOption(_ option: RSOption) { - if let optionPlugin = self.find(pluginType: RSOptionPlugin.self) { - optionPlugin.option = option - } else { - let optionPlugin = RSOptionPlugin() - optionPlugin.option = option - add(plugin: optionPlugin) - } - } -} diff --git a/Sources/Classes/Client/Plugins/RSReplayQueuePlugin.swift b/Sources/Classes/Client/Plugins/RSReplayQueuePlugin.swift index 132dd407..8aadbfa3 100644 --- a/Sources/Classes/Client/Plugins/RSReplayQueuePlugin.swift +++ b/Sources/Classes/Client/Plugins/RSReplayQueuePlugin.swift @@ -9,46 +9,66 @@ import Foundation internal class RSReplayQueuePlugin: RSPlugin { - static let maxSize = 1000 - - @RSAtomic var running: Bool = true - let type: PluginType = .before + weak var client: RSClient? - var client: RSClient? - - let syncQueue = DispatchQueue(label: "replayQueue.rudder.com") - var queuedEvents = [RSMessage]() + private let syncQueue = DispatchQueue(label: "replayQueue.rudder.com") + private var queuedEvents = [RSMessage]() + @RSAtomic var running = true + static let maxSize = 1000 required init() { } func execute(message: T?) -> T? { - if running == true, let e = message { - syncQueue.sync { - if queuedEvents.count >= Self.maxSize { - queuedEvents.removeFirst() + if running, let e = message { + syncQueue.async { [weak self] in + guard let self = self else { return } + if self.queuedEvents.count >= Self.maxSize { + self.queuedEvents.removeFirst() } - queuedEvents.append(e) + self.queuedEvents.append(e) } - return nil } return message } func update(serverConfig: RSServerConfig, type: UpdateType) { - guard client?.checkServerConfigInProgress == true else { return } - running = false - replayEvents() + if type == .refresh { + replayEvents(isSourceEnabled: serverConfig.enabled) + } else if type == .initial, serverConfig.enabled { + // if source config is cached and source is enabled. we no longer need to add the events to queue. + running = false + } } } extension RSReplayQueuePlugin { - internal func replayEvents() { - syncQueue.sync { - for event in queuedEvents { - client?.process(message: event) + internal func replayEvents(isSourceEnabled: Bool) { + syncQueue.async { [weak self] in + guard let self = self else { return } + // send events to device mode destinations only. + // we will wait for 2 seconds to finish the destinations to be initialized. + DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(2)) { [weak self] in + guard let self = self else { return } + self.running = false + if isSourceEnabled { + if let destinationPlugins = self.getExternalDestinationPlugins() { + for event in self.queuedEvents { + destinationPlugins.forEach { plugin in + _ = plugin.execute(message: event) + } + } + } + } + self.queuedEvents.removeAll() } - queuedEvents.removeAll() + } } + + private func getExternalDestinationPlugins() -> [RSDestinationPlugin]? { + return (self.client?.controller.plugins[.destination]?.plugins as? [RSDestinationPlugin])?.filter({ plugin in + return plugin.key != RUDDER_DESTINATION_KEY + }) + } } diff --git a/Sources/Classes/Client/Plugins/RSSessionStoragePlugin.swift b/Sources/Classes/Client/Plugins/RSSessionStoragePlugin.swift new file mode 100644 index 00000000..5884529b --- /dev/null +++ b/Sources/Classes/Client/Plugins/RSSessionStoragePlugin.swift @@ -0,0 +1,43 @@ +// +// RSSessionStoragePlugin.swift +// Rudder +// +// Created by Pallab Maiti on 07/12/23. +// Copyright © 2023 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import Foundation + +// This class has no use at the moment. But will be needed when multiple instance will come in place. We will remove RSSessionStorage +class RSSessionStoragePlugin: RSPlatformPlugin { + let type: PluginType = .before + var client: RSClient? + + private var deviceToken: String? + private var advertisingId: String? + private var appTrackingConsent: RSAppTrackingConsent? + private var option: RSOption? + + func execute(message: T?) -> T? { + guard var workingMessage = message else { return message } + if let messageIntegrations = workingMessage.option?.integrations { + workingMessage.integrations = messageIntegrations + if messageIntegrations["All"] == nil { + workingMessage.integrations?["All"] = true + } + } else if let globalOption = option { + if let integrations = globalOption.integrations { + workingMessage.integrations = integrations + if integrations["All"] == nil { + workingMessage.integrations?["All"] = true + } + } else { + workingMessage.integrations = ["All": true] + } + } else { + workingMessage.integrations = ["All": true] + } + return message + } + +} diff --git a/Sources/Classes/Client/Plugins/RSUserIdPlugin.swift b/Sources/Classes/Client/Plugins/RSUserIdPlugin.swift deleted file mode 100644 index 11ce8301..00000000 --- a/Sources/Classes/Client/Plugins/RSUserIdPlugin.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// RSUserIdPlugin.swift -// RudderStack -// -// Created by Pallab Maiti on 01/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSUserIdPlugin: RSPlatformPlugin { - let type = PluginType.before - var client: RSClient? { - didSet { - initialSetup() - } - } - - var userId: String? - - required init() { } - - func initialSetup() { - userId = RSUserDefaults.getUserId() - } - - func execute(message: T?) -> T? { - guard var workingMessage = message else { return message } - if let userId = userId { - workingMessage.userId = userId - if var context = workingMessage.context { - context[keyPath: "traits.userId"] = userId - workingMessage.context = context - client?.updateContext(context) - } - } - return workingMessage - } -} - -extension RSUserIdPlugin: RSEventPlugin { - func reset() { - userId = nil - RSUserDefaults.saveUserId(nil) - } -} - -extension RSClient { - internal func setUserId(_ userId: String) { - if let userIdPlugin = self.find(pluginType: RSUserIdPlugin.self) { - userIdPlugin.userId = userId - RSUserDefaults.saveUserId(userId) - } else { - let userIdPlugin = RSUserIdPlugin() - userIdPlugin.userId = userId - RSUserDefaults.saveUserId(userId) - add(plugin: userIdPlugin) - } - } -} - -extension AliasMessage { - internal func applyAlias(newId: String, client: RSClient) -> Self { - var result: Self = self - result.userId = newId - if let userIdPlugin = client.find(pluginType: RSUserIdPlugin.self), let previousId = userIdPlugin.userId { - result.previousId = previousId - } - return result - } -} diff --git a/Sources/Classes/Client/Plugins/RSUserSessionPlugin.swift b/Sources/Classes/Client/Plugins/RSUserSessionPlugin.swift index 9ea2158f..c1f219d7 100644 --- a/Sources/Classes/Client/Plugins/RSUserSessionPlugin.swift +++ b/Sources/Classes/Client/Plugins/RSUserSessionPlugin.swift @@ -15,25 +15,39 @@ class RSUserSessionPlugin: RSPlatformPlugin, RSEventPlugin { } } + private var userDefaults: RSUserDefaults? private var sessionTimeOut: Int? - private var isNewSessionStarted: Bool = false + private var isNewSessionStarted = false - private var sessionId: Int? - private var lastEventTimeStamp: Int? + var sessionId: Int? { + userDefaults?.read(.sessionId) + } + + private var lastEventTimeStamp: Int? { + userDefaults?.read(.lastEventTimeStamp) + } - private var isAutomaticSessionTrackingStatus = false - private var isSessionStoppedStatus = false - private var isManualSessionTrackingStatus = false + private var automaticSessionTrackingStatus: Bool { + userDefaults?.read(.automaticSessionTrackingStatus) ?? false + } + + private var sessionStoppedStatus: Bool { + userDefaults?.read(.sessionStoppedStatus) ?? false + } + + private var manualSessionTrackingStatus: Bool { + userDefaults?.read(.manualSessionTrackingStatus) ?? false + } private var isSessionTrackingAllowed: Bool { - if !isSessionStoppedStatus && (isManualSessionTrackingStatus || isAutomaticSessionTrackingAllowed) { + if !sessionStoppedStatus && (manualSessionTrackingStatus || isAutomaticSessionTrackingAllowed) { return true } return false } private var isAutomaticSessionTrackingAllowed: Bool { - guard let clientConfig = client?.config, clientConfig.trackLifecycleEvents, clientConfig.automaticSessionTracking else { + guard let config = client?.config, config.trackLifecycleEvents, config.automaticSessionTracking else { return false } return true @@ -42,40 +56,34 @@ class RSUserSessionPlugin: RSPlatformPlugin, RSEventPlugin { required init() {} func initialSetup() { - self.sessionTimeOut = client?.config?.sessionTimeout - self.sessionId = RSUserDefaults.getSessionId() - self.lastEventTimeStamp = RSUserDefaults.getLastEventTimeStamp() + guard let client = self.client else { return } + sessionTimeOut = client.config?.sessionTimeout + userDefaults = client.userDefaults if isAutomaticSessionTrackingAllowed { - if let previousAutomaticSessionTrackingStatus = RSUserDefaults.getAutomaticSessionTrackingStatus(), - (isSessionExpired() || !previousAutomaticSessionTrackingStatus) { - startNewSession(nil) - RSUserDefaults.saveAutomaticSessionTrackingStatus(true) - RSUserDefaults.saveManualSessionTrackingStatus(false) - RSUserDefaults.saveSessionStoppedStatus(false) + if isSessionExpired() || !automaticSessionTrackingStatus { + startNewSession() + userDefaults?.write(.automaticSessionTrackingStatus, value: true) + userDefaults?.write(.manualSessionTrackingStatus, value: false) + userDefaults?.write(.sessionStoppedStatus, value: false) } } else { - RSUserDefaults.saveAutomaticSessionTrackingStatus(false) + userDefaults?.write(.automaticSessionTrackingStatus, value: false) } - refreshSesionParams() } func execute(message: T?) -> T? { guard var workingMessage = message else { return message } if isSessionTrackingAllowed { - if var context = workingMessage.context, - let sessionId = self.sessionId { - context[keyPath: "sessionId"] = sessionId + if let sessionId = self.sessionId { + workingMessage.sessionId = sessionId if isNewSessionStarted { - context[keyPath: "sessionStart"] = true + workingMessage.sessionStart = true isNewSessionStarted = false } - workingMessage.context = context - client?.updateContext(context) } let currentEventTimeStamp = RSUtils.getTimeStamp() - RSUserDefaults.saveLastEventTimeStamp(currentEventTimeStamp) - self.lastEventTimeStamp = currentEventTimeStamp + userDefaults?.write(.lastEventTimeStamp, value: currentEventTimeStamp) } return workingMessage } @@ -90,53 +98,42 @@ class RSUserSessionPlugin: RSPlatformPlugin, RSEventPlugin { return timeDifference >= Double(sessionTimeOut / 1000) } - func startNewSession(_ newSessionId: Int?) { - var sessionId: Int = newSessionId ?? RSUtils.getTimeStamp() - client?.log(message: "New session is started", logLevel: .verbose) + func startNewSession(_ sessionId: Int? = nil) { isNewSessionStarted = true - RSUserDefaults.saveSessionId(sessionId) - self.sessionId = sessionId - } - - private func refreshSesionParams() { - self.isAutomaticSessionTrackingStatus = RSUserDefaults.getAutomaticSessionTrackingStatus() ?? false - self.isManualSessionTrackingStatus = RSUserDefaults.getManualSessionTrackingStatus() ?? false - self.isSessionStoppedStatus = RSUserDefaults.getSessionStoppedStatus() ?? false - self.lastEventTimeStamp = RSUserDefaults.getLastEventTimeStamp() + userDefaults?.write(.sessionId, value: sessionId ?? RSUtils.getTimeStamp()) + Logger.log(message: "New session is started", logLevel: .verbose) } } extension RSUserSessionPlugin { - func startManualSession(_ sessionId: Int?) { - RSUserDefaults.saveAutomaticSessionTrackingStatus(false) - RSUserDefaults.saveManualSessionTrackingStatus(true) - RSUserDefaults.saveSessionStoppedStatus(false) - RSUserDefaults.saveLastEventTimeStamp(nil) - - refreshSesionParams() + func startManualSession(_ sessionId: Int? = nil) { + userDefaults?.write(.automaticSessionTrackingStatus, value: false) + userDefaults?.write(.manualSessionTrackingStatus, value: true) + userDefaults?.write(.sessionStoppedStatus, value: false) + userDefaults?.remove(.lastEventTimeStamp) startNewSession(sessionId) } func endSession() { - RSUserDefaults.saveAutomaticSessionTrackingStatus(false) - RSUserDefaults.saveManualSessionTrackingStatus(false) - RSUserDefaults.saveSessionStoppedStatus(true) - RSUserDefaults.saveLastEventTimeStamp(nil) - - refreshSesionParams() + userDefaults?.write(.automaticSessionTrackingStatus, value: false) + userDefaults?.write(.manualSessionTrackingStatus, value: false) + userDefaults?.write(.sessionStoppedStatus, value: true) + userDefaults?.remove(.lastEventTimeStamp) } func reset() { if isSessionTrackingAllowed { - RSUserDefaults.saveLastEventTimeStamp(nil) - startNewSession(nil) + if automaticSessionTrackingStatus { + userDefaults?.write(.lastEventTimeStamp, value: RSUtils.getTimeStamp()) + } + startNewSession() } } func refreshSessionIfNeeded() { - if isSessionTrackingAllowed, isAutomaticSessionTrackingStatus, isSessionExpired() { - startNewSession(nil) + if isSessionTrackingAllowed, automaticSessionTrackingStatus, isSessionExpired() { + startNewSession() } } } @@ -153,22 +150,22 @@ extension RSClient { @objc public func startSession() { if let userSessionPlugin = self.find(pluginType: RSUserSessionPlugin.self) { - userSessionPlugin.startManualSession(nil) + userSessionPlugin.startManualSession() } else { - log(message: "SDK is not yet initialised. Hence manual session cannot be started", logLevel: .debug) + Logger.log(message: "SDK is not yet initialised. Hence manual session cannot be started", logLevel: .debug) } } @objc public func startSession(_ sessionId: Int) { guard String(sessionId).count >= 10 else { - log(message: "RSClient: startSession: Length of the sessionId should be at least 10: \(sessionId)", logLevel: .error) + Logger.log(message: "RSClient: startSession: Length of the sessionId should be at least 10: \(sessionId)", logLevel: .error) return } if let userSessionPlugin = self.find(pluginType: RSUserSessionPlugin.self) { userSessionPlugin.startManualSession(sessionId) } else { - log(message: "SDK is not yet initialised. Hence manual session cannot be started", logLevel: .debug) + Logger.log(message: "SDK is not yet initialised. Hence manual session cannot be started", logLevel: .debug) } } diff --git a/Sources/Classes/Client/Plugins/RudderDestinationPlugin.swift b/Sources/Classes/Client/Plugins/RudderDestinationPlugin.swift index 47bcf879..483d34c4 100644 --- a/Sources/Classes/Client/Plugins/RudderDestinationPlugin.swift +++ b/Sources/Classes/Client/Plugins/RudderDestinationPlugin.swift @@ -10,9 +10,9 @@ import Foundation class RudderDestinationPlugin: RSDestinationPlugin { let type = PluginType.destination - let key: String = "RudderStack" + let key: String = RUDDER_DESTINATION_KEY let controller = RSController() - var client: RSClient? { + weak var client: RSClient? { didSet { initialSetup() } @@ -23,28 +23,106 @@ class RudderDestinationPlugin: RSDestinationPlugin { private var databaseManager: RSDatabaseManager? private var serviceManager: RSServiceManager? + private var config: RSConfig? + private var userDefaults: RSUserDefaults? + + @RSAtomic var isSourceEnabled = false + @RSAtomic var isFlushingStarted = false private let lock = NSLock() func initialSetup() { - guard let client = self.client, let config = client.config else { return } + guard let client = self.client else { return } + config = client.config + userDefaults = client.userDefaults databaseManager = RSDatabaseManager(client: client) - serviceManager = RSServiceManager(client: client) - flushTimer = RSRepeatingTimer(interval: TimeInterval(config.sleepTimeOut)) { [weak self] in - guard let self = self else { return } - self.periodicFlush() + if isUnitTesting { + let configuration = URLSessionConfiguration.default + configuration.protocolClasses = [MockURLProtocol.self] + let urlSession = URLSession.init(configuration: configuration) + serviceManager = RSServiceManager(urlSession: urlSession, client: client) + + let data = """ + { + + } + """.data(using: .utf8) + + MockURLProtocol.requestHandler = { _ in + let response = HTTPURLResponse(url: URL(string: "https://some.rudderstack.com.url")!, statusCode: 500, httpVersion: nil, headerFields: nil)! + return (response, data) + } + } else { + serviceManager = RSServiceManager(client: client) } } func execute(message: T?) -> T? { let result: T? = message if let r = result { - let modified = configureCloudDestinations(message: r) - queueEvent(message: modified) + saveEvent(message: r) } return result } + func update(serverConfig: RSServerConfig, type: UpdateType) { + if type == .refresh { + if isSourceEnabled, !serverConfig.enabled { + // if source was enabled before, but it has been disabled now; then cancel flushing + Logger.logDebug("Source has been disabled in your dashboard. Flushing canceled.") + flushTimer?.cancel() + } else if !isSourceEnabled { + if serverConfig.enabled { + // if source was disabled before, but it has been enabled now; then resume flushing + Logger.logDebug("Source has been enabled in your dashboard. Flushing resumed.") + flushTimer?.resume() + } else { + // if source was disabled before, still it has been disabled now; then cancel flushing + Logger.logDebug("Source is still disabled in your dashboard. Flushing canceled.") + flushTimer?.cancel() + } + } + } + isSourceEnabled = serverConfig.enabled + startFlushing() + } + + func startFlushing() { + guard !isFlushingStarted else { + return + } + isFlushingStarted = true + Logger.logDebug("Flushing started.") + var sleepCount = 0 + flushTimer = RSRepeatingTimer(interval: TimeInterval(1)) { [weak self] in + guard let self = self else { return } + self.uploadsQueue.async { + guard self.isSourceEnabled else { + // if source is disabled; then suspend flushing + Logger.logDebug("Source is disabled in your dashboard. Flushing suspended.") + self.flushTimer?.suspend() + return + } + guard let recordCount = self.databaseManager?.getDBRecordCount(), let config = self.config else { + return + } + if recordCount >= config.dbCountThreshold || sleepCount >= config.sleepTimeOut { + if recordCount > 0 { + self.flushTimer?.suspend() + self.flush(sleepCount: 0) { _ in + sleepCount = 0 + self.flushTimer?.resume() + } + } else { + sleepCount = 0 + } + } else { + sleepCount += 1 + } + } + } + } + internal func enterForeground() { flushTimer?.resume() } @@ -58,137 +136,143 @@ class RudderDestinationPlugin: RSDestinationPlugin { enterBackground() } - private func queueEvent(message: T) { + private func saveEvent(message: T) { guard let databaseManager = self.databaseManager else { return } databaseManager.write(message) } } extension RudderDestinationPlugin { - internal func configureCloudDestinations(message: T) -> T { - guard let serverConfig = client?.serverConfig else { return message } - guard let plugins = client?.controller.plugins[.destination]?.plugins as? [RSDestinationPlugin] else { return message } - guard let customerValues = message.integrations else { return message } - - var merged = [String: Bool]() - - for plugin in plugins { - var hasSettings = false - if let destinations = serverConfig.destinations { - if let destination = destinations.first(where: { $0.destinationDefinition?.displayName == plugin.key }), destination.enabled { - hasSettings = true - } - } - if hasSettings { - merged[plugin.key] = false + + func flush() { + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + guard let self = self, let databaseManager = self.databaseManager, let config = self.config else { + return } + let totalBatchCount = RSUtils.getNumberOfBatch(from: databaseManager.getDBRecordCount(), and: config.flushQueueSize) + self.flushBatch(index: 0, totalBatchCount: totalBatchCount) } - - for (key, value) in customerValues { - merged[key] = value - } - - var modified = message - modified.integrations = merged - - return modified } -} - -extension RudderDestinationPlugin { - func flush() { - DispatchQueue.global(qos: .userInitiated).async { [weak self] in - guard let self = self, let databaseManager = self.databaseManager, let config = self.client?.config else { + private func flushBatch(index: Int, totalBatchCount: Int) { + guard index < totalBatchCount else { + Logger.logDebug("All batches have been flushed successfully") + return + } + Logger.log(message: "Flushing batch \(index + 1)/\(totalBatchCount)", logLevel: .debug) + flush(retryCount: 0) { [weak self] result in + guard let self = self else { return } - let uploadGroup = DispatchGroup() - let numberOfBatch = RSUtils.getNumberOfBatch(from: databaseManager.getDBRecordCount(), and: config.flushQueueSize) - for i in 0..) { + let maxRetryCount = 3 + + guard retryCount < maxRetryCount else { + completion(.failure(NSError(code: .SERVER_ERROR))) + return + } + + flushEventsToServer { result in + self.lock.unlock() + switch result { + case .success: + completion(.success(true)) + case .failure(let error): + if error.code == RSErrorCode.WRONG_WRITE_KEY.rawValue { + Logger.log(message: "Wrong write key", logLevel: .error) + completion(.failure(error)) + } else { + Logger.log(message: "Failed to flush batch \(index + 1)/\(totalBatchCount), \(3 - retryCount) retries left", logLevel: .debug) + DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(retryCount)) { + flush(retryCount: retryCount + 1, completion: completion) + } + } } - uploadGroup.leave() } - uploadGroup.wait() } } - func periodicFlush() { - uploadsQueue.async { [weak self] in - guard let self = self else { return } - self.prepareEventsToFlush() + private func flush(sleepCount: Int, completion: @escaping Handler) { + flushEventsToServer { result in + self.lock.unlock() + switch result { + case .success: + completion(.success(true)) + case .failure(let error): + let errorCode = RSErrorCode(rawValue: error.code) + switch errorCode { + case .SERVER_ERROR: + DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(sleepCount + 1)) { + self.flush(sleepCount: sleepCount + 1, completion: completion) + } + default: + Logger.logError("Aborting flush. Error code: \((errorCode ?? RSErrorCode.UNKNOWN).rawValue)") + completion(.failure(NSError(code: .UNKNOWN))) + } + } } } - - @discardableResult - func prepareEventsToFlush() -> RSErrorCode? { + + private func flushEventsToServer(_ completion: Handler? = nil) { lock.lock() - guard let databaseManager = databaseManager, let config = client?.config else { - return .UNKNOWN + guard let databaseManager = databaseManager, let config = config else { + completion?(.failure(NSError(code: .UNKNOWN))) + return } - var errorCode: RSErrorCode? let recordCount = databaseManager.getDBRecordCount() - client?.log(message: "DBRecordCount \(recordCount)", logLevel: .debug) + Logger.log(message: "DBRecordCount \(recordCount)", logLevel: .debug) if recordCount > config.dbCountThreshold { - client?.log(message: "Old DBRecordCount \(recordCount - config.dbCountThreshold)", logLevel: .debug) + Logger.log(message: "Old DBRecordCount \(recordCount - config.dbCountThreshold)", logLevel: .debug) let dbMessage = databaseManager.getEvents(recordCount - config.dbCountThreshold) if let messageIds = dbMessage?.messageIds { databaseManager.removeEvents(messageIds) } } - client?.log(message: "Fetching events to flush to sever", logLevel: .debug) + Logger.log(message: "Fetching events to flush to sever", logLevel: .debug) guard let dbMessage = databaseManager.getEvents(config.flushQueueSize) else { - return .UNKNOWN + completion?(.failure(NSError(code: .UNKNOWN))) + return } - if dbMessage.messages.isEmpty == false { + if !dbMessage.messages.isEmpty { let params = RSUtils.getJSON(from: dbMessage) - client?.log(message: "Payload: \(params)", logLevel: .debug) - client?.log(message: "EventCount: \(dbMessage.messages.count)", logLevel: .debug) + Logger.log(message: "Payload: \(params)", logLevel: .debug) + Logger.log(message: "EventCount: \(dbMessage.messages.count)", logLevel: .debug) if !params.isEmpty { - errorCode = self.flushEventsToServer(params: params) - if errorCode == nil { - client?.log(message: "clearing events from DB", logLevel: .debug) - databaseManager.removeEvents(dbMessage.messageIds) - } else if errorCode == .WRONG_WRITE_KEY { - client?.log(message: "Wrong WriteKey. Aborting.", logLevel: .error) - } else if errorCode == .SERVER_ERROR { - client?.log(message: "Server error. Aborting.", logLevel: .error) - } + serviceManager?.flushEvents(params: params, { result in + switch result { + case .success: + Logger.log(message: "clearing events from DB", logLevel: .debug) + databaseManager.removeEvents(dbMessage.messageIds) + completion?(.success(true)) + case .failure(let error): + completion?(.failure(error)) + } + }) + } else { + completion?(.failure(NSError(code: .UNKNOWN))) } + } else { + completion?(.failure(NSError(code: .UNKNOWN))) } - lock.unlock() - return errorCode } - func flushEventsToServer(params: String) -> RSErrorCode? { - var errorCode: RSErrorCode? - let semaphore = DispatchSemaphore(value: 0) - serviceManager?.flushEvents(params: params) { result in - switch result { - case .success: - errorCode = nil - case .failure(let error): - errorCode = RSErrorCode(rawValue: error.code) + private func periodicFlush() { + uploadsQueue.async { [weak self] in + guard let self = self else { return } + self.flushEventsToServer { _ in + self.lock.unlock() } - semaphore.signal() } - semaphore.wait() - return errorCode } } diff --git a/Sources/Classes/Client/RSClient+Plugins.swift b/Sources/Classes/Client/RSClient+Plugins.swift index 196583f3..c93149cd 100644 --- a/Sources/Classes/Client/RSClient+Plugins.swift +++ b/Sources/Classes/Client/RSClient+Plugins.swift @@ -12,14 +12,9 @@ extension RSClient { internal func addPlugins() { add(plugin: RSReplayQueuePlugin()) - - let logPlugin = RSLoggerPlugin() - logPlugin.loggingEnabled(config?.logLevel != RSLogLevel.none) - add(plugin: logPlugin) - add(plugin: RSIntegrationPlugin()) add(plugin: RudderDestinationPlugin()) - add(plugin: RSGDPRPlugin()) + add(plugin: RSUserSessionPlugin()) if let platformPlugins = platformPlugins() { for plugin in platformPlugins { @@ -34,15 +29,7 @@ extension RSClient { var plugins = [RSPlatformPlugin]() plugins.append(RSContextPlugin()) - - plugins.append(RSIdentifyTraitsPlugin()) - plugins.append(RSAliasIdPlugin()) - plugins.append(RSUserIdPlugin()) - plugins.append(RSAnonymousIdPlugin()) - plugins.append(RSAppTrackingConsentPlugin()) - plugins.append(RSAdvertisingIdPlugin()) - plugins.append(RSUserSessionPlugin()) - + plugins += Vendor.current.requiredPlugins if config?.trackLifecycleEvents == true { @@ -77,32 +64,22 @@ extension RSClient { } } -#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) +#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) || os(watchOS) import UIKit extension RSClient { internal func setupServerConfigCheck() { - checkServerConfig() - NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { (notification) in - guard let app = notification.object as? UIApplication else { return } - if app.applicationState == .background { - self.checkServerConfig() - } - } - } -} -#elseif os(watchOS) -extension RSClient { - internal func setupServerConfigCheck() { - checkServerConfig() + setupDownloadServerConfig() } } + #elseif os(macOS) import Cocoa extension RSClient { internal func setupServerConfigCheck() { - checkServerConfig() - RSRepeatingTimer.schedule(interval: .days(1), queue: .main) { - self.checkServerConfig() + setupDownloadServerConfig() + RSRepeatingTimer.schedule(interval: .days(1), queue: .main) { [weak self] in + guard let self = self else { return } + self.setupDownloadServerConfig() } } } @@ -116,70 +93,93 @@ extension RSClient { } func update(plugin: RSPlugin, serverConfig: RSServerConfig, type: UpdateType) { - plugin.update(serverConfig: serverConfig, type: type) + // if the server config is not cached. we send the updateType to external destination as initial + var updateType = type + if !isServerConfigCached, type == .refresh, let destination = plugin as? RSDestinationPlugin, destination.key != RUDDER_DESTINATION_KEY { + updateType = .initial + } + plugin.update(serverConfig: serverConfig, type: updateType) if let dest = plugin as? RSDestinationPlugin { - dest.apply { (subPlugin) in - subPlugin.update(serverConfig: serverConfig, type: type) + dest.apply { subPlugin in + subPlugin.update(serverConfig: serverConfig, type: updateType) } } } - func checkServerConfig() { - assert(Thread.isMainThread) - guard !self.checkServerConfigInProgress else { return } - - self.checkServerConfigInProgress = true - self.checkServerConfig(retryCount: 0) { - assert(Thread.isMainThread) - self.checkServerConfigInProgress = false + func setupDownloadServerConfig() { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + + self.downloadServerConfig(retryCount: 0) { } } } - private func checkServerConfig(retryCount: Int, completion: @escaping ( ) -> Void) { - assert(Thread.isMainThread) + private func downloadServerConfig(retryCount: Int, completion: @escaping () -> Void) { + if isUnitTesting { + checkServerConfigForUnitTesting(completion: completion) + return + } let maxRetryCount = 4 guard retryCount < maxRetryCount else { - log(message: "Server config download failed. Using last stored config from storage", logLevel: .debug) + Logger.log(message: "Server config download failed.", logLevel: .debug) completion() return } - let updateType: UpdateType = self.serverConfig == nil ? .initial : .refresh - - fetchServerConfig { result in - assert(Thread.isMainThread) - + serviceManager?.downloadServerConfig({ [weak self] result in + guard let self = self else { return } switch result { case .success(let serverConfig): + Logger.log(message: "Server config download successful.", logLevel: .debug) self.serverConfig = serverConfig - self.update(serverConfig: serverConfig, type: updateType) - RSUserDefaults.saveServerConfig(serverConfig) - RSUserDefaults.updateLastUpdatedTime(RSUtils.getTimeStamp()) - self.log(message: "server config download successful", logLevel: .debug) + self.update(serverConfig: serverConfig, type: .refresh) + self.userDefaults.write(.serverConfig, value: serverConfig) + self.isServerConfigCached = true completion() - + case .failure(let error): if error.code == RSErrorCode.WRONG_WRITE_KEY.rawValue { - self.log(message: "Wrong write key", logLevel: .error) - self.checkServerConfig(retryCount: maxRetryCount, completion: completion) + Logger.log(message: "Wrong write key", logLevel: .error) + self.downloadServerConfig(retryCount: maxRetryCount, completion: completion) } else { - self.log(message: "Retrying download in \(retryCount) seconds", logLevel: .debug) + Logger.log(message: "Retrying download in \(retryCount) seconds", logLevel: .debug) DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(retryCount)) { - self.checkServerConfig(retryCount: retryCount + 1, completion: completion) + self.downloadServerConfig(retryCount: retryCount + 1, completion: completion) } } } - } + }) } - private func fetchServerConfig(completion: @escaping (HandlerResult) -> Void) { - let serviceManager = RSServiceManager(client: self) - serviceManager.downloadServerConfig { result in - DispatchQueue.main.async { - completion(result) - } + private func checkServerConfigForUnitTesting(completion: @escaping () -> Void) { + var configFileName = "" + switch RSClient.sourceConfigType { + default: + configFileName = "ServerConfig" } + + let path = TestUtils.shared.getPath(forResource: configFileName, ofType: "json") + do { + let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) + let serverConfig = try JSONDecoder().decode(RSServerConfig.self, from: data) + self.serverConfig = serverConfig + self.update(serverConfig: serverConfig, type: .initial) + self.userDefaults.write(.serverConfig, value: serverConfig) + } catch { } + + completion() } } + +extension RSClient { + static var sourceConfigType: SourceConfigType = .standard +} + +// swiftlint:disable inclusive_language +enum SourceConfigType { + case whiteList + case blackList + case standard +} diff --git a/Sources/Classes/Client/RSClient.swift b/Sources/Classes/Client/RSClient.swift index f1f4322b..2c3229af 100644 --- a/Sources/Classes/Client/RSClient.swift +++ b/Sources/Classes/Client/RSClient.swift @@ -8,16 +8,30 @@ import Foundation +// swiftlint:disable file_length @objc open class RSClient: NSObject { var config: RSConfig? var controller: RSController var serverConfig: RSServerConfig? - internal var checkServerConfigInProgress = false + var serviceManager: RSServiceManager? + @RSAtomic var isServerConfigCached = false static let shared = RSClient() + var userDefaults = RSUserDefaults() + var userInfo: RSUserInfo? { + let userId: String? = userDefaults.read(.userId) + let traits: JSON? = userDefaults.read(.traits) + var anonymousId: String? = userDefaults.read(.anonymousId) + if anonymousId == nil { + anonymousId = RSUtils.getUniqueId() + userDefaults.write(.anonymousId, value: anonymousId) + } + return RSUserInfo(anonymousId: anonymousId, userId: userId, traits: traits) + } private override init() { - serverConfig = RSUserDefaults.getServerConfig() + serverConfig = userDefaults.read(.serverConfig) + isServerConfigCached = serverConfig != nil controller = RSController() } @@ -43,7 +57,14 @@ open class RSClient: NSObject { */ @objc public func configure(with config: RSConfig) { + // Config can be set only one time per session. + guard self.config == nil else { return } self.config = config + Logger.logLevel = config.logLevel + if config.writeKey.isEmpty { + Logger.logError("Invalid writeKey: Provided writeKey is empty") + } + serviceManager = RSServiceManager(client: self) addPlugins() } @@ -69,6 +90,11 @@ open class RSClient: NSObject { _track(eventName, properties: properties, option: nil) } + @objc + public func track(_ eventName: String, option: RSOption) { + _track(eventName, properties: nil, option: option) + } + @objc public func track(_ eventName: String) { _track(eventName, properties: nil, option: nil) @@ -96,6 +122,11 @@ open class RSClient: NSObject { _identify(userId, traits: traits, option: nil) } + @objc + public func identify(_ userId: String, option: RSOption) { + _identify(userId, traits: nil, option: option) + } + @objc public func identify(_ userId: String) { _identify(userId, traits: nil, option: nil) @@ -123,6 +154,11 @@ open class RSClient: NSObject { _screen(screenName, category: category, properties: properties, option: nil) } + @objc + public func screen(_ screenName: String, properties: ScreenProperties, option: RSOption) { + _screen(screenName, category: nil, properties: properties, option: option) + } + @objc public func screen(_ screenName: String, properties: ScreenProperties) { _screen(screenName, category: nil, properties: properties, option: nil) @@ -133,6 +169,11 @@ open class RSClient: NSObject { _screen(screenName, category: category, properties: nil, option: nil) } + @objc + public func screen(_ screenName: String, option: RSOption) { + _screen(screenName, category: nil, properties: nil, option: option) + } + @objc public func screen(_ screenName: String) { _screen(screenName, category: nil, properties: nil, option: nil) @@ -160,6 +201,11 @@ open class RSClient: NSObject { _group(groupId, traits: traits, option: nil) } + @objc + public func group(_ groupId: String, option: RSOption) { + _group(groupId, traits: nil, option: option) + } + @objc public func group(_ groupId: String) { _group(groupId, traits: nil, option: nil) @@ -189,18 +235,25 @@ open class RSClient: NSObject { extension RSClient { internal func _track(_ eventName: String, properties: TrackProperties? = nil, option: RSOption? = nil) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOutAndEventDrop, logLevel: .debug) + return + } guard eventName.isNotEmpty else { - log(message: "eventName can not be empty", logLevel: .warning) + Logger.log(message: "eventName can not be empty", logLevel: .warning) return } let message = TrackMessage(event: eventName, properties: properties, option: option) - .applyRawEventData() - process(message: message) + process(incomingMessage: message) } internal func _screen(_ screenName: String, category: String? = nil, properties: ScreenProperties? = nil, option: RSOption? = nil) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOutAndEventDrop, logLevel: .debug) + return + } guard screenName.isNotEmpty else { - log(message: "screenName can not be empty", logLevel: .warning) + Logger.log(message: "screenName can not be empty", logLevel: .warning) return } var screenProperties = ScreenProperties() @@ -209,43 +262,62 @@ extension RSClient { } screenProperties["name"] = screenName let message = ScreenMessage(title: screenName, category: category, properties: screenProperties, option: option) - .applyRawEventData() - process(message: message) + process(incomingMessage: message) } internal func _group(_ groupId: String, traits: [String: String]? = nil, option: RSOption? = nil) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOutAndEventDrop, logLevel: .debug) + return + } guard groupId.isNotEmpty else { - log(message: "groupId can not be empty", logLevel: .warning) + Logger.log(message: "groupId can not be empty", logLevel: .warning) return } let message = GroupMessage(groupId: groupId, traits: traits, option: option) - .applyRawEventData() - process(message: message) + process(incomingMessage: message) } internal func _alias(_ newId: String, option: RSOption? = nil) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOutAndEventDrop, logLevel: .debug) + return + } guard newId.isNotEmpty else { - log(message: "newId can not be empty", logLevel: .warning) + Logger.log(message: "newId can not be empty", logLevel: .warning) return } - let message = AliasMessage(newId: newId, option: option) - .applyAlias(newId: newId, client: self) - .applyRawEventData() - setAlias(newId) - setUserId(newId) - process(message: message) + let previousId: String? = userDefaults.read(.userId) + userDefaults.write(.userId, value: newId) + var dict: [String: Any] = ["id": newId] + if let json: JSON = userDefaults.read(.traits), let traits = json.dictionaryValue { + dict.merge(traits) { (_, new) in new } + } + userDefaults.write(.traits, value: try? JSON(dict)) + let message = AliasMessage(newId: newId, previousId: previousId, option: option) + process(incomingMessage: message) } internal func _identify(_ userId: String, traits: IdentifyTraits? = nil, option: RSOption? = nil) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOutAndEventDrop, logLevel: .debug) + return + } guard userId.isNotEmpty else { - log(message: "userId can not be empty", logLevel: .warning) + Logger.log(message: "userId can not be empty", logLevel: .warning) return } + userDefaults.write(.userId, value: userId) + + if let traits = traits { + userDefaults.write(.traits, value: try? JSON(traits)) + } + + if let externalIds = option?.externalIds { + userDefaults.write(.externalId, value: try? JSON(externalIds)) + } let message = IdentifyMessage(userId: userId, traits: traits, option: option) - .applyRawEventData() - setTraits(traits, userId) - setUserId(userId) - process(message: message) + process(incomingMessage: message) } } @@ -257,10 +329,11 @@ extension RSClient { */ @objc public var anonymousId: String? { - if let anonymousIdPlugin = self.find(pluginType: RSAnonymousIdPlugin.self) { - return anonymousIdPlugin.anonymousId + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return nil } - return nil + return userDefaults.read(.anonymousId) } /** @@ -268,32 +341,39 @@ extension RSClient { */ @objc public var userId: String? { - if let userIdPlugin = self.find(pluginType: RSUserIdPlugin.self) { - return userIdPlugin.userId + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return nil } - return nil + return userDefaults.read(.userId) } /** Returns the context that were specified in the last call. */ @objc - public var context: MessageContext? { - if let contextPlugin = self.find(pluginType: RSContextPlugin.self) { - return contextPlugin.context + public var context: RSContext? { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return nil } - return nil + if let currentContext: RSContext = RSSessionStorage.shared.read(.context) { + return currentContext + } + return RSContext(userDefaults: userDefaults) } /** Returns the traits that were specified in the last identify call. */ @objc - public var traits: MessageTraits? { - if let contextPlugin = self.find(pluginType: RSContextPlugin.self) { - return contextPlugin.traits + public var traits: IdentifyTraits? { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return nil } - return nil + let traits: JSON? = RSContext.traits(userDefaults: userDefaults) + return traits?.dictionaryValue } /** @@ -308,11 +388,24 @@ extension RSClient { } } + /** + API for reset current slate. Traits, UserID's, anonymousId, etc are all cleared or reset. This command will also be sent to each destination present in the system. + */ + @objc + public func reset(and refreshAnonymousId: Bool) { + if refreshAnonymousId { + userDefaults.write(.anonymousId, value: RSUtils.getUniqueId()) + } + reset() + } + /** API for reset current slate. Traits, UserID's, anonymousId, etc are all cleared or reset. This command will also be sent to each destination present in the system. */ @objc public func reset() { + userDefaults.reset() + RSSessionStorage.shared.reset() apply { plugin in if let p = plugin as? RSEventPlugin { p.reset() @@ -333,8 +426,27 @@ extension RSClient { */ @objc public var configuration: RSConfig? { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return nil + } return config } + + /** + Returns id of an active session. + */ + @objc + public var sessionId: String? { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return nil + } + if let userSessionPlugin = self.find(pluginType: RSUserSessionPlugin.self), let sessionId = userSessionPlugin.sessionId { + return "\(sessionId)" + } + return nil + } } extension RSClient { @@ -365,7 +477,17 @@ extension RSClient { } extension RSClient { + func process(incomingMessage: RSMessage) { + let message = incomingMessage.applyRawEventData(userInfo: userInfo) + process(message: message) + } + func process(message: RSMessage) { + if let serverConfig = serverConfig, !serverConfig.enabled { + Logger.logDebug("Source is disabled in your dashboard. Hence event is dropped.") + return + } + switch message { case let e as TrackMessage: controller.process(incomingEvent: e) @@ -382,3 +504,127 @@ extension RSClient { } } } + +extension RSClient { + /** + API for setting unique identifier of every call. + - Parameters: + - anonymousId: Unique identifier of every event + # Example # + ``` + RSClient.sharedInstance().setAnonymousId("sample_anonymous_id") + ``` + */ + @objc + public func setAnonymousId(_ anonymousId: String) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return + } + guard anonymousId.isNotEmpty else { + Logger.log(message: "anonymousId can not be empty", logLevel: .warning) + return + } + userDefaults.write(.anonymousId, value: anonymousId) + } + + /** + API for setting enable/disable sending the events across all the event calls made using the SDK to the specified destinations. + - Parameters: + - option: Options related to every API call + # Example # + ``` + let defaultOption = RSOption() + defaultOption.putIntegration("Amplitude", isEnabled: true) + + RSClient.sharedInstance().setOption(defaultOption) + ``` + */ + @objc + public func setOption(_ option: RSOption) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return + } + RSSessionStorage.shared.write(.option, value: option) + } + + /** + API for setting token under context.device.token. + - Parameters: + - token: Token of the device + # Example # + ``` + RSClient.sharedInstance().setDeviceToken("sample_device_token") + ``` + */ + @objc + public func setDeviceToken(_ token: String) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return + } + guard token.isNotEmpty else { + Logger.log(message: "token can not be empty", logLevel: .warning) + return + } + RSSessionStorage.shared.write(.deviceToken, value: token) + } + + /** + API for setting identifier under context.device.advertisingId. + - Parameters: + - advertisingId: IDFA value + # Example # + ``` + RSClient.sharedInstance().setAdvertisingId("sample_advertising_id") + ``` + */ + @objc + public func setAdvertisingId(_ advertisingId: String) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return + } + guard advertisingId.isNotEmpty else { + Logger.log(message: "advertisingId can not be empty", logLevel: .warning) + return + } + if advertisingId != "00000000-0000-0000-0000-000000000000" { + RSSessionStorage.shared.write(.advertisingId, value: advertisingId) + } + } + + /** + API for app tracking consent management. + - Parameters: + - appTrackingConsent: App tracking consent + # Example # + ``` + RSClient.sharedInstance().setAppTrackingConsent(.authorize) + ``` + */ + @objc + public func setAppTrackingConsent(_ appTrackingConsent: RSAppTrackingConsent) { + if let optOutStatus: Bool = userDefaults.read(.optStatus), optOutStatus { + Logger.log(message: LogMessages.optOut, logLevel: .debug) + return + } + RSSessionStorage.shared.write(.appTrackingConsent, value: appTrackingConsent) + } + + /** + API for enable or disable tracking user activities. + - Parameters: + - status: Enable or disable tracking + # Example # + ``` + RSClient.sharedInstance().setOptOutStatus(false) + ``` + */ + @objc + public func setOptOutStatus(_ status: Bool) { + userDefaults.write(.optStatus, value: status) + Logger.log(message: "User has been Opted \(status ? "out" : "in")", logLevel: .debug) + } +} diff --git a/Sources/Classes/Client/RSController.swift b/Sources/Classes/Client/RSController.swift index 5b72cd54..8ae5ca8b 100644 --- a/Sources/Classes/Client/RSController.swift +++ b/Sources/Classes/Client/RSController.swift @@ -67,8 +67,7 @@ class Mediator { plugins.forEach { (plugin) in if let r = result { - // Drop the event return because we don't care about the - // final result. + // Drop the event return because we don't care about the final result. if plugin is RSDestinationPlugin { _ = plugin.execute(message: r) } else { @@ -191,7 +190,7 @@ extension RSDestinationPlugin { } func isDestinationEnabled(message: RSMessage) -> Bool { - var customerDisabled = false + var customerDisabled: Bool? if let integration = message.integrations?.first(where: { key, _ in return key == self.key @@ -199,14 +198,23 @@ extension RSDestinationPlugin { customerDisabled = true } - var hasSettings = false + var hasSettings: Bool? if let destinations = client?.serverConfig?.destinations { if let destination = destinations.first(where: { $0.destinationDefinition?.displayName == self.key }), destination.enabled { hasSettings = true } } - return (hasSettings == true && customerDisabled == false) + if customerDisabled == nil, hasSettings == nil { + return false + } + if customerDisabled == true { + return false + } + if hasSettings == true { + return true + } + return false } // swiftlint:disable inclusive_language @@ -215,8 +223,6 @@ extension RSDestinationPlugin { switch message { case let e as TrackMessage: return list.contains(e.event) - case let e as ScreenMessage: - return list.contains(e.name) default: break } @@ -244,41 +250,32 @@ extension RSDestinationPlugin { } func process(incomingEvent: E) -> E? { - // This will process plugins (think destination middleware) that are tied - // to this destination. - - var result: E? + guard isDestinationEnabled(message: incomingEvent) else { + Logger.logDebug("Destination is not enabled.") + return nil + } - if isDestinationEnabled(message: incomingEvent) { - // check event is allowed - if isEventAllowed(message: incomingEvent) { - // apply .before and .enrichment types first ... - let beforeResult = controller.applyPlugins(type: .before, event: incomingEvent) - let enrichmentResult = controller.applyPlugins(type: .enrichment, event: beforeResult) - - // now we execute any overrides we may have made. basically, the idea is to take an - // incoming event, like identify, and map it to whatever is appropriate for this destination. - var destinationResult: E? - switch enrichmentResult { - case let e as IdentifyMessage: - destinationResult = identify(message: e) as? E - case let e as TrackMessage: - destinationResult = track(message: e) as? E - case let e as ScreenMessage: - destinationResult = screen(message: e) as? E - case let e as GroupMessage: - destinationResult = group(message: e) as? E - case let e as AliasMessage: - destinationResult = alias(message: e) as? E - default: - break - } + guard isEventAllowed(message: incomingEvent) else { + Logger.logDebug("Event is filtered by Client-side event filtering.") + return nil + } - // apply .after plugins ... - result = controller.applyPlugins(type: .after, event: destinationResult) - } + var destinationResult: E? + switch incomingEvent { + case let e as IdentifyMessage: + destinationResult = identify(message: e) as? E + case let e as TrackMessage: + destinationResult = track(message: e) as? E + case let e as ScreenMessage: + destinationResult = screen(message: e) as? E + case let e as GroupMessage: + destinationResult = group(message: e) as? E + case let e as AliasMessage: + destinationResult = alias(message: e) as? E + default: + break } - return result + return destinationResult } } diff --git a/Sources/Classes/Client/RSDatabaseManager.swift b/Sources/Classes/Client/RSDatabaseManager.swift index d5c9be7b..d745ea70 100644 --- a/Sources/Classes/Client/RSDatabaseManager.swift +++ b/Sources/Classes/Client/RSDatabaseManager.swift @@ -25,17 +25,17 @@ class RSDatabaseManager { func createTable() { var createTableStatement: OpaquePointer? let createTableString = "CREATE TABLE IF NOT EXISTS events( id INTEGER PRIMARY KEY AUTOINCREMENT, message TEXT NOT NULL, updated INTEGER NOT NULL);" - client.log(message: "createTableSQL: \(createTableString)", logLevel: .debug) + Logger.log(message: "createTableSQL: \(createTableString)", logLevel: .debug) if sqlite3_prepare_v2(database, createTableString, -1, &createTableStatement, nil) == SQLITE_OK { if sqlite3_step(createTableStatement) == SQLITE_DONE { - client.log(message: ("DB Schema created"), logLevel: .debug) + Logger.log(message: ("DB Schema created"), logLevel: .debug) } else { - client.log(message: "DB Schema creation error", logLevel: .error) + Logger.log(message: "DB Schema creation error", logLevel: .error) } } else { let errorMessage = String(cString: sqlite3_errmsg(database)) - client.log(message: "DB Schema CREATE statement is not prepared, Reason: \(errorMessage)", logLevel: .error) + Logger.log(message: "DB Schema CREATE statement is not prepared, Reason: \(errorMessage)", logLevel: .error) } sqlite3_finalize(createTableStatement) } @@ -46,15 +46,15 @@ class RSDatabaseManager { if sqlite3_prepare_v2(database, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK { sqlite3_bind_text(insertStatement, 1, ((message.replacingOccurrences(of: "'", with: "''")) as NSString).utf8String, -1, nil) sqlite3_bind_int(insertStatement, 2, Int32(RSUtils.getTimeStamp())) - client.log(message: "saveEventSQL: \(insertStatementString)", logLevel: .debug) + Logger.log(message: "saveEventSQL: \(insertStatementString)", logLevel: .debug) if sqlite3_step(insertStatement) == SQLITE_DONE { - client.log(message: "Event inserted to table", logLevel: .debug) + Logger.log(message: "Event inserted to table", logLevel: .debug) } else { - client.log(message: "Event insertion error", logLevel: .error) + Logger.log(message: "Event insertion error", logLevel: .error) } } else { let errorMessage = String(cString: sqlite3_errmsg(database)) - client.log(message: "Event INSERT statement is not prepared, Reason: \(errorMessage)", logLevel: .error) + Logger.log(message: "Event INSERT statement is not prepared, Reason: \(errorMessage)", logLevel: .error) } sqlite3_finalize(insertStatement) } @@ -62,16 +62,16 @@ class RSDatabaseManager { private func clearEvents(_ messageIds: [String]) { var deleteStatement: OpaquePointer? let deleteStatementString = "DELETE FROM events WHERE id IN (\((messageIds as NSArray).componentsJoined(by: ",") as NSString));" - client.log(message: "deleteEventSQL: \(deleteStatementString)", logLevel: .debug) + Logger.log(message: "deleteEventSQL: \(deleteStatementString)", logLevel: .debug) if sqlite3_prepare_v2(database, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK { if sqlite3_step(deleteStatement) == SQLITE_DONE { - client.log(message: "Events deleted from DB", logLevel: .debug) + Logger.log(message: "Events deleted from DB", logLevel: .debug) } else { - client.log(message: "Event deletion error", logLevel: .error) + Logger.log(message: "Event deletion error", logLevel: .error) } } else { let errorMessage = String(cString: sqlite3_errmsg(database)) - client.log(message: "Event DELETE statement is not prepared, Reason: \(errorMessage)", logLevel: .error) + Logger.log(message: "Event DELETE statement is not prepared, Reason: \(errorMessage)", logLevel: .error) } sqlite3_finalize(deleteStatement) } @@ -80,7 +80,7 @@ class RSDatabaseManager { var queryStatement: OpaquePointer? var message: RSDBMessage? let queryStatementString = "SELECT * FROM events ORDER BY updated ASC LIMIT \(count);" - client.log(message: "countSQL: \(queryStatementString)", logLevel: .debug) + Logger.log(message: "countSQL: \(queryStatementString)", logLevel: .debug) if sqlite3_prepare_v2(database, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK { var messages = [String]() var messageIds = [String]() @@ -95,7 +95,7 @@ class RSDatabaseManager { message = RSDBMessage(messages: messages, messageIds: messageIds) } else { let errorMessage = String(cString: sqlite3_errmsg(database)) - client.log(message: "Event SELECT statement is not prepared, Reason: \(errorMessage)", logLevel: .error) + Logger.log(message: "Event SELECT statement is not prepared, Reason: \(errorMessage)", logLevel: .error) } sqlite3_finalize(queryStatement) return message @@ -104,16 +104,16 @@ class RSDatabaseManager { private func fetchDBRecordCount() -> Int { var queryStatement: OpaquePointer? let queryStatementString = "SELECT COUNT(*) FROM 'events'" - client.log(message: "countSQL: \(queryStatementString)", logLevel: .debug) + Logger.log(message: "countSQL: \(queryStatementString)", logLevel: .debug) var count = 0 if sqlite3_prepare_v2(database, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK { - client.log(message: "count fetched from DB", logLevel: .debug) + Logger.log(message: "count fetched from DB", logLevel: .debug) while sqlite3_step(queryStatement) == SQLITE_ROW { count = Int(sqlite3_column_int(queryStatement, 0)) } } else { let errorMessage = String(cString: sqlite3_errmsg(database)) - client.log(message: "count SELECT statement is not prepared, Reason: \(errorMessage)", logLevel: .error) + Logger.log(message: "count SELECT statement is not prepared, Reason: \(errorMessage)", logLevel: .error) } sqlite3_finalize(queryStatement) return count @@ -123,15 +123,15 @@ class RSDatabaseManager { var deleteStatement: OpaquePointer? let deleteStatementString = "DELETE FROM 'events';" if sqlite3_prepare_v2(database, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK { - client.log(message: "deleteEventSQL: \(deleteStatementString)", logLevel: .debug) + Logger.log(message: "deleteEventSQL: \(deleteStatementString)", logLevel: .debug) if sqlite3_step(deleteStatement) == SQLITE_DONE { - client.log(message: "Events deleted from DB", logLevel: .debug) + Logger.log(message: "Events deleted from DB", logLevel: .debug) } else { - client.log(message: "Event deletion error", logLevel: .error) + Logger.log(message: "Event deletion error", logLevel: .error) } } else { let errorMessage = String(cString: sqlite3_errmsg(database)) - client.log(message: "Event DELETE statement is not prepared, Reason: \(errorMessage)", logLevel: .error) + Logger.log(message: "Event DELETE statement is not prepared, Reason: \(errorMessage)", logLevel: .error) } sqlite3_finalize(deleteStatement) } @@ -143,24 +143,23 @@ extension RSDatabaseManager { guard let self = self else { return } self.lock.lock() do { - let jsonObject = RSUtils.handleUrlAndDateTypes(message.dictionaryValue) - if JSONSerialization.isValidJSONObject(jsonObject) { + if let jsonObject = RSUtils.handleUrlAndDateTypes(message.dictionaryValue), JSONSerialization.isValidJSONObject(jsonObject) { let jsonData = try JSONSerialization.data(withJSONObject: jsonObject) if let jsonString = String(data: jsonData, encoding: .utf8) { - self.client.log(message: "dump: \(jsonString)", logLevel: .debug) + Logger.log(message: "dump: \(jsonString)", logLevel: .debug) if jsonString.getUTF8Length() > MAX_EVENT_SIZE { - self.client.log(message: "dump: Event size exceeds the maximum permitted event size \(MAX_EVENT_SIZE)", logLevel: .error) + Logger.log(message: "dump: Event size exceeds the maximum permitted event size \(MAX_EVENT_SIZE)", logLevel: .error) return } self.saveEvent(jsonString) } else { - self.client.log(message: "dump: Can not convert to JSON", logLevel: .error) + Logger.log(message: "dump: Can not convert to JSON", logLevel: .error) } } else { - self.client.log(message: "dump: Not a valid JSON object", logLevel: .error) + Logger.log(message: "dump: Not a valid JSON object", logLevel: .error) } } catch { - self.client.log(message: "dump: \(error.localizedDescription)", logLevel: .error) + Logger.log(message: "dump: \(error.localizedDescription)", logLevel: .error) } self.lock.unlock() } diff --git a/Sources/Classes/Common/Constants/LogMessages.swift b/Sources/Classes/Common/Constants/LogMessages.swift new file mode 100644 index 00000000..4b56429f --- /dev/null +++ b/Sources/Classes/Common/Constants/LogMessages.swift @@ -0,0 +1,14 @@ +// +// LogMessages.swift +// Rudder +// +// Created by Pallab Maiti on 28/11/23. +// Copyright © 2023 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import Foundation + +struct LogMessages { + static let optOut = "User has been Opted out" + static let optOutAndEventDrop = "User has been Opted out, hence dropping the event" +} diff --git a/Sources/Classes/Common/Constants/RSConstants.swift b/Sources/Classes/Common/Constants/RSConstants.swift index a6e7bd22..75cc669b 100644 --- a/Sources/Classes/Common/Constants/RSConstants.swift +++ b/Sources/Classes/Common/Constants/RSConstants.swift @@ -8,15 +8,8 @@ import Foundation -public let RSDataPlaneUrl = "https://hosted.rudderlabs.com" -public let RSFlushQueueSize: Int = 30 -public let RSDBCountThreshold: Int = 10000 -public let RSSleepTimeout: Int = 10 -public let RSControlPlaneUrl = "https://api.rudderlabs.com" -public let RSTrackLifeCycleEvents = true -public let RSRecordScreenViews = false -let RETRY_FLUSH_COUNT = 3 let TAG = "RudderStack" +let RUDDER_DESTINATION_KEY = "RudderStack" let RSServerConfigKey = "rs_server_config" let RSServerLastUpdatedKey = "rs_server_last_updated" let RSTraitsKey = "rs_traits" @@ -28,13 +21,31 @@ let RSAnonymousIdKey = "rs_anonymous_id" let RSOptStatusKey = "rs_opt_status" let RSOptInTimeKey = "rs_opt_in_time" let RSOptOutTimeKey = "rs_opt_out_time" -let MAX_EVENT_SIZE: UInt = 32 * 1024 -let MAX_BATCH_SIZE: UInt = 500 * 1024 -let RSAutoSessionTracking: Bool = true -let RSSessionTimeout: Int = 300000 -let RSSessionInActivityMinimumTimeOut = 0 let RSSessionIdKey = "rl_session_id" let RSLastEventTimeStamp = "rl_last_event_time_stamp" let RSSessionAutoTrackStatus = "rl_session_auto_track_status" let RSSessionManualTrackStatus = "rl_session_manual_track_status" let RSSessionStoppedStatus = "rl_session_stopped_status" + +let DEFAULT_DATA_PLANE_URL = "https://hosted.rudderlabs.com" +let DEFAULT_CONTROL_PLANE_URL = "https://api.rudderlabs.com" +let DEFAULT_TRACK_LIFE_CYCLE_EVENTS_STATUS = true +let DEFAULT_RECORD_SCREEN_VIEWS_STATUS = false +let DEFAULT_AUTO_SESSION_TRACKING_STATUS = true + +let DEFAULT_FLUSH_QUEUE_SIZE = 30 +let MAX_FLUSH_QUEUE_SIZE = 100 +let MIN_FLUSH_QUEUE_SIZE = 1 + +let DEFAULT_DB_COUNT_THRESHOLD = 10000 +let MIN_DB_COUNT_THRESHOLD = 1 + +let DEFAULT_SLEEP_TIMEOUT = 10 +let MIN_SLEEP_TIMEOUT = 10 + +let DEFAULT_SESSION_TIMEOUT: Int = 300000 +let MIN_SESSION_TIMEOUT = 0 + +let MAX_EVENT_SIZE: UInt = 32 * 1024 +let MAX_BATCH_SIZE: UInt = 500 * 1024 +let RETRY_FLUSH_COUNT = 3 diff --git a/Sources/Classes/Common/Constants/RSUserDefaults.swift b/Sources/Classes/Common/Constants/RSUserDefaults.swift deleted file mode 100644 index e80b31ba..00000000 --- a/Sources/Classes/Common/Constants/RSUserDefaults.swift +++ /dev/null @@ -1,133 +0,0 @@ -// -// RSUserDefaults.swift -// RudderStack -// -// Created by Pallab Maiti on 17/08/21. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSUserDefaults { - static func getLastUpdatedTime() -> Int? { - return UserDefaults.standard.lastUpdateTime - } - - static func updateLastUpdatedTime(_ time: Int) { - UserDefaults.standard.lastUpdateTime = time - } - - static func getServerConfig() -> RSServerConfig? { - return UserDefaults.standard.serverConfig - } - - static func saveServerConfig(_ serverConfig: RSServerConfig) { - UserDefaults.standard.serverConfig = serverConfig - } - - static func getApplicationVersion() -> String? { - return UserDefaults.standard.applicationVersion - } - - static func saveApplicationVersion(_ version: String?) { - UserDefaults.standard.applicationVersion = version - } - - static func getApplicationBuild() -> String? { - return UserDefaults.standard.applicationBuild - } - - static func saveApplicationBuild(_ build: String?) { - UserDefaults.standard.applicationBuild = build - } - - static func getOptStatus() -> Bool? { - return UserDefaults.standard.optStatus - } - - static func saveOptStatus(_ optStatus: Bool) { - UserDefaults.standard.optStatus = optStatus - } - - static func getOptInTime() -> Int? { - return UserDefaults.standard.optInTime - } - - static func updateOptInTime(_ optInTime: Int?) { - UserDefaults.standard.optInTime = optInTime - } - - static func getOptOutTime() -> Int? { - return UserDefaults.standard.optOutTime - } - - static func updateOptOutTime(_ optOutTime: Int?) { - UserDefaults.standard.optOutTime = optOutTime - } - - static func saveSessionId(_ sessionId: Int) { - UserDefaults.standard.sessionId = sessionId - UserDefaults.standard.synchronize() - } - - static func getSessionId() -> Int? { - return UserDefaults.standard.sessionId - } - - static func saveLastEventTimeStamp(_ lastEventTimeStamp: Int?) { - UserDefaults.standard.lastEventTimeStamp = lastEventTimeStamp - UserDefaults.standard.synchronize() - } - - static func getLastEventTimeStamp() -> Int? { - if let lastEventTimeStamp = UserDefaults.standard.lastEventTimeStamp, lastEventTimeStamp != 0 { - return lastEventTimeStamp - } - return nil - } - - static func saveAutomaticSessionTrackingStatus(_ autoTrackingStatus: Bool) { - UserDefaults.standard.automaticTrackingStatus = autoTrackingStatus - UserDefaults.standard.synchronize() - } - - static func getAutomaticSessionTrackingStatus() -> Bool? { - return UserDefaults.standard.automaticTrackingStatus - } - - static func saveManualSessionTrackingStatus(_ manualTrackingStatus: Bool) { - UserDefaults.standard.manualTrackingStatus = manualTrackingStatus - UserDefaults.standard.synchronize() - } - - static func getManualSessionTrackingStatus() -> Bool? { - return UserDefaults.standard.manualTrackingStatus - } - - static func saveSessionStoppedStatus(_ sessionStoppedStatus: Bool) { - UserDefaults.standard.sessionStoppedStatus = sessionStoppedStatus - UserDefaults.standard.synchronize() - } - - static func getSessionStoppedStatus() -> Bool? { - return UserDefaults.standard.sessionStoppedStatus - } - - static func saveTraits(_ traits: Data?) { - UserDefaults.standard.traits = traits - UserDefaults.standard.synchronize() - } - - static func getTraits() -> Data? { - return UserDefaults.standard.traits - } - - static func saveUserId(_ userId: String?) { - UserDefaults.standard.userId = userId - UserDefaults.standard.synchronize() - } - - static func getUserId() -> String? { - return UserDefaults.standard.userId - } -} diff --git a/Sources/Classes/Common/Extensions/Data+Ext.swift b/Sources/Classes/Common/Extensions/Data+Ext.swift new file mode 100644 index 00000000..fc59b20b --- /dev/null +++ b/Sources/Classes/Common/Extensions/Data+Ext.swift @@ -0,0 +1,16 @@ +// +// Data+Ext.swift +// Rudder +// +// Created by Pallab Maiti on 16/07/22. +// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import Foundation + +extension Data { + var hexString: String { + let hexString = map { String(format: "%02.2hhx", $0) }.joined() + return hexString + } +} diff --git a/Sources/Classes/Common/Extensions/UserDefaults+Ext.swift b/Sources/Classes/Common/Extensions/UserDefaults+Ext.swift deleted file mode 100644 index 33743d8a..00000000 --- a/Sources/Classes/Common/Extensions/UserDefaults+Ext.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// UserDefaults+Ext.swift -// RudderStack -// -// Created by Pallab Maiti on 13/08/21. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -extension PropertyListDecoder { - func optionalDecode(_ type: T.Type, from object: Any?) -> T? { - if let data = object as? Data { - return try? PropertyListDecoder().decode(T.self, from: data) - } - return nil - } -} - -extension UserDefaults { - var serverConfig: RSServerConfig? { - get { - return PropertyListDecoder().optionalDecode(RSServerConfig.self, from: object(forKey: RSServerConfigKey)) - } - set { - if let newValue = newValue { - set(try? PropertyListEncoder().encode(newValue), forKey: RSServerConfigKey) - } else { - set(nil, forKey: RSServerConfigKey) - } - } - } - - var lastUpdateTime: Int? { - get { integer(forKey: RSServerLastUpdatedKey) } - set { setValue(newValue, forKey: RSServerLastUpdatedKey) } - } - - var applicationVersion: String? { - get { string(forKey: RSApplicationVersionKey) } - set { setValue(newValue, forKey: RSApplicationVersionKey) } - } - - var applicationBuild: String? { - get { string(forKey: RSApplicationBuildKey) } - set { setValue(newValue, forKey: RSApplicationBuildKey) } - } - - var externalIds: String? { - get { string(forKey: RSExternalIdKey) } - set { setValue(newValue, forKey: RSExternalIdKey) } - } - - var anonymousId: String? { - get { string(forKey: RSAnonymousIdKey) } - set { setValue(newValue, forKey: RSAnonymousIdKey) } - } - - var optStatus: Bool? { - get { bool(forKey: RSOptStatusKey) } - set { setValue(newValue, forKey: RSOptStatusKey) } - } - - var optInTime: Int? { - get { integer(forKey: RSOptInTimeKey) } - set { setValue(newValue, forKey: RSOptInTimeKey) } - } - - var optOutTime: Int? { - get { integer(forKey: RSOptOutTimeKey) } - set { setValue(newValue, forKey: RSOptOutTimeKey) } - } - - var sessionId: Int? { - get { integer(forKey: RSSessionIdKey) } - set { setValue(newValue, forKey: RSSessionIdKey) } - } - - var lastEventTimeStamp: Int? { - get { integer(forKey: RSLastEventTimeStamp) } - set { setValue(newValue, forKey: RSLastEventTimeStamp) } - } - - var automaticTrackingStatus: Bool? { - get { bool(forKey: RSSessionAutoTrackStatus) } - set { setValue(newValue, forKey: RSSessionAutoTrackStatus) } - } - - var manualTrackingStatus: Bool? { - get { bool(forKey: RSSessionManualTrackStatus) } - set { setValue(newValue, forKey: RSSessionManualTrackStatus) } - } - - var sessionStoppedStatus: Bool? { - get { bool(forKey: RSSessionStoppedStatus) } - set { setValue(newValue, forKey: RSSessionStoppedStatus) } - } - - var traits: Data? { - get { data(forKey: RSTraitsKey) } - set { setValue(newValue, forKey: RSTraitsKey) } - } - - var userId: String? { - get { string(forKey: RSUserIdKey) } - set { setValue(newValue, forKey: RSUserIdKey) } - } -} diff --git a/Sources/Classes/Domain/Enums/JSON.swift b/Sources/Classes/Domain/Enums/JSON.swift index 96fd2e05..f177ccda 100644 --- a/Sources/Classes/Domain/Enums/JSON.swift +++ b/Sources/Classes/Domain/Enums/JSON.swift @@ -35,7 +35,8 @@ public enum JSON: Equatable { } // For primitives?? - public init(_ value: Any) throws { + // swiftlint:disable cyclomatic_complexity + public init(_ value: Any) throws { switch value { // handle NS values case _ as NSNull: @@ -61,11 +62,22 @@ public enum JSON: Equatable { self = .object(try object.mapValues(JSON.init)) case let json as JSON: self = json + case let date as Date: + self = .string(Self.dateString(from: date)) // we don't work with whatever is being supplied default: throw JSONError.nonJSONType(type: "\(value.self)") } } + + static func dateString(from date: Date) -> String { + let dateFormatter = DateFormatter() + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + dateFormatter.locale = Locale(identifier: "en_US_POSIX") + dateFormatter.calendar = Calendar(identifier: Calendar.Identifier.gregorian) + return dateFormatter.string(from: date) + } } // MARK: - Codable conformance diff --git a/Sources/Classes/Domain/Enums/RSErrorCode.swift b/Sources/Classes/Domain/Enums/RSErrorCode.swift index c3ebeee1..772eef2c 100644 --- a/Sources/Classes/Domain/Enums/RSErrorCode.swift +++ b/Sources/Classes/Domain/Enums/RSErrorCode.swift @@ -12,5 +12,7 @@ enum RSErrorCode: Int { case UNKNOWN = -1 case WRONG_WRITE_KEY = 0 case DECODING_FAILED = 1 + case RESOURCE_NOT_FOUND = 2 + case BAD_REQUEST = 3 case SERVER_ERROR = 500 } diff --git a/Sources/Classes/Domain/Enums/RSLogLevel.swift b/Sources/Classes/Domain/Enums/RSLogLevel.swift deleted file mode 100644 index 0ad9d3d9..00000000 --- a/Sources/Classes/Domain/Enums/RSLogLevel.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// RSLogLevel.swift -// RudderStack -// -// Created by Pallab Maiti on 10/08/21. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -@frozen @objc public enum RSLogLevel: Int { - case verbose = 5 - case debug = 4 - case info = 3 - case warning = 2 - case error = 1 - case `none` = 0 - - public func toString() -> String { - switch self { - case .verbose: - return "Verbose" - case .debug: - return "Debug" - case .info: - return "Info" - case .warning: - return "Warning" - case .error: - return "Error" - case .none: - return "" - } - } -} diff --git a/Sources/Classes/Domain/Models/RSConfig.swift b/Sources/Classes/Domain/Models/RSConfig.swift index 3ff3b212..d4173392 100644 --- a/Sources/Classes/Domain/Models/RSConfig.swift +++ b/Sources/Classes/Domain/Models/RSConfig.swift @@ -15,52 +15,52 @@ open class RSConfig: NSObject { return _writeKey } - private var _dataPlaneUrl: String = RSDataPlaneUrl + private var _dataPlaneUrl: String = DEFAULT_DATA_PLANE_URL public var dataPlaneUrl: String { return _dataPlaneUrl } - private var _flushQueueSize: Int = RSFlushQueueSize + private var _flushQueueSize: Int = DEFAULT_FLUSH_QUEUE_SIZE public var flushQueueSize: Int { return _flushQueueSize } - private var _dbCountThreshold: Int = RSDBCountThreshold + private var _dbCountThreshold: Int = DEFAULT_DB_COUNT_THRESHOLD public var dbCountThreshold: Int { return _dbCountThreshold } - private var _sleepTimeOut: Int = RSSleepTimeout + private var _sleepTimeOut: Int = DEFAULT_SLEEP_TIMEOUT public var sleepTimeOut: Int { return _sleepTimeOut } - private var _logLevel: RSLogLevel = RSLogLevel.none + private var _logLevel: RSLogLevel = .error public var logLevel: RSLogLevel { return _logLevel } - private var _trackLifecycleEvents: Bool = RSTrackLifeCycleEvents + private var _trackLifecycleEvents: Bool = DEFAULT_TRACK_LIFE_CYCLE_EVENTS_STATUS public var trackLifecycleEvents: Bool { return _trackLifecycleEvents } - private var _recordScreenViews: Bool = RSRecordScreenViews + private var _recordScreenViews: Bool = DEFAULT_RECORD_SCREEN_VIEWS_STATUS public var recordScreenViews: Bool { return _recordScreenViews } - private var _controlPlaneUrl: String = RSControlPlaneUrl + private var _controlPlaneUrl: String = DEFAULT_CONTROL_PLANE_URL public var controlPlaneUrl: String { return _controlPlaneUrl } - private var _autoSessionTracking: Bool = RSAutoSessionTracking + private var _autoSessionTracking: Bool = DEFAULT_AUTO_SESSION_TRACKING_STATUS public var automaticSessionTracking: Bool { return _autoSessionTracking } - private var _sessionTimeout: Int = RSSessionTimeout + private var _sessionTimeout: Int = DEFAULT_SESSION_TIMEOUT public var sessionTimeout: Int { return _sessionTimeout } @@ -72,20 +72,24 @@ open class RSConfig: NSObject { @discardableResult @objc public func dataPlaneURL(_ dataPlaneUrl: String) -> RSConfig { - if let url = URL(string: dataPlaneUrl) { - if let scheme = url.scheme, let host = url.host { - if let port = url.port { - _dataPlaneUrl = "\(scheme)://\(host):\(port)" - } else { - _dataPlaneUrl = "\(scheme)://\(host)" - } - } + guard let url = URL(string: dataPlaneUrl), let scheme = url.scheme, let host = url.host else { + Logger.logError("dataPlaneUrl is invalid") + return self + } + if let port = url.port { + _dataPlaneUrl = "\(scheme)://\(host):\(port)" + } else { + _dataPlaneUrl = "\(scheme)://\(host)" } return self } @discardableResult @objc public func flushQueueSize(_ flushQueueSize: Int) -> RSConfig { + guard flushQueueSize >= MIN_FLUSH_QUEUE_SIZE && flushQueueSize <= MAX_FLUSH_QUEUE_SIZE else { + Logger.logError("flushQueueSize is out of range. Min: 1, Max: 100. Set to default") + return self + } _flushQueueSize = flushQueueSize return self } @@ -98,14 +102,18 @@ open class RSConfig: NSObject { @discardableResult @objc public func dbCountThreshold(_ dbCountThreshold: Int) -> RSConfig { + guard dbCountThreshold >= MIN_DB_COUNT_THRESHOLD else { + Logger.logError("dbCountThreshold is invalid. Min: 1. Set to default") + return self + } _dbCountThreshold = dbCountThreshold return self } @discardableResult @objc public func sleepTimeOut(_ sleepTimeOut: Int) -> RSConfig { - guard sleepTimeOut > 0 else { - RSClient.rsLog(message: "sleepTimeOut can not be less than 1 second", logLevel: .warning) + guard sleepTimeOut >= MIN_SLEEP_TIMEOUT else { + Logger.logError("sleepTimeOut is invalid. Min: 10. Set to default") return self } _sleepTimeOut = sleepTimeOut @@ -126,14 +134,14 @@ open class RSConfig: NSObject { @discardableResult @objc public func controlPlaneURL(_ controlPlaneUrl: String) -> RSConfig { - if let url = URL(string: controlPlaneUrl) { - if let scheme = url.scheme, let host = url.host { - if let port = url.port { - _controlPlaneUrl = "\(scheme)://\(host):\(port)" - } else { - _controlPlaneUrl = "\(scheme)://\(host)" - } - } + guard let url = URL(string: controlPlaneUrl), let scheme = url.scheme, let host = url.host else { + Logger.logError("controlPlaneUrl is invalid") + return self + } + if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) { + _controlPlaneUrl = "\(scheme)://\(host)\(url.path())" + } else { + _controlPlaneUrl = "\(scheme)://\(host)\(url.path)" } return self } @@ -146,8 +154,8 @@ open class RSConfig: NSObject { @discardableResult @objc public func sessionTimeout(_ sessionTimeout: Int) -> RSConfig { - guard sessionTimeout >= RSSessionInActivityMinimumTimeOut else { - RSClient.rsLog(message: "sessionTimeout can not be less than 0 second", logLevel: .warning) + guard sessionTimeout >= MIN_SESSION_TIMEOUT else { + Logger.logError("sessionTimeout is invalid. Min: 0. Set to default") return self } _sessionTimeout = sessionTimeout diff --git a/Sources/Classes/Domain/Models/RSContext.swift b/Sources/Classes/Domain/Models/RSContext.swift new file mode 100644 index 00000000..a166b0b7 --- /dev/null +++ b/Sources/Classes/Domain/Models/RSContext.swift @@ -0,0 +1,216 @@ +// +// RSContext.swift +// Rudder +// +// Created by Pallab Maiti on 07/12/23. +// Copyright © 2023 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import Foundation + +@objc +open class RSContext: NSObject, Codable { + @objc + open class AppInfo: NSObject, Codable { + public var name: String + public var namespace: String + public var build: String + public var version: String + + internal override init() { + let info = Bundle.main.infoDictionary + name = (info?["CFBundleDisplayName"] as? String ?? (info?["CFBundleName"] as? String)) ?? "" + version = (info?["CFBundleShortVersionString"] as? String) ?? "" + build = (info?["CFBundleVersion"] as? String) ?? "" + namespace = (Bundle.main.bundleIdentifier) ?? "" + } + } + + @objc + open class DeviceInfo: NSObject, Codable { + public var id: String? + public var manufacturer: String? + public var model: String? + public var name: String? + public var type: String? + + internal override init() { + manufacturer = Vendor.current.manufacturer + type = Vendor.current.type + model = Vendor.current.model + name = Vendor.current.name + id = Vendor.current.identifierForVendor + } + } + + @objc + open class LibraryInfo: NSObject, Codable { + public var name: String + public var version: String + + internal override init() { + name = "rudder-ios-library" + version = RSVersion + } + } + + @objc + open class OSInfo: NSObject, Codable { + public var name: String? + public var version: String? + + internal override init() { + name = Vendor.current.systemName + version = Vendor.current.systemVersion + } + } + + @objc + open class ScreenInfo: NSObject, Codable { + public var density: Double? + public var width: Double? + public var height: Double? + + internal override init() { + width = Vendor.current.screenSize.width + height = Vendor.current.screenSize.height + density = Vendor.current.screenSize.density + } + } + + @objc + open class NetworkInfo: NSObject, Codable { + public var carrier: String? + public var bluetooth: Bool = false + public var cellular: Bool = false + public var wifi: Bool = false + + internal override init() { + switch Vendor.current.connection { + case .online(.cellular): + cellular = true + case .online(.wifi): + wifi = true + case .online(.bluetooth): + bluetooth = true + default: + break + } + carrier = Vendor.current.carrier + } + } + + private let _app: AppInfo? + public var app: AppInfo? { + _app + } + + private let _device: DeviceInfo? + public var device: DeviceInfo? { + _device + } + + private let _library: LibraryInfo? + public var library: LibraryInfo? { + _library + } + + private let _os: OSInfo? + public var os: OSInfo? { + _os + } + + private let _screen: ScreenInfo? + public var screen: ScreenInfo? { + _screen + } + + private let _locale: String? + public var locale: String? { + _locale + } + + private let _network: NetworkInfo? + public var network: NetworkInfo? { + _network + } + + private let _timezone: String? + public var timezone: String? { + _timezone + } + + private let _traits: JSON? + public var traits: IdentifyTraits? { + _traits?.dictionaryValue + } + + private let _externalIds: [[String: String]]? + public var externalIds: [[String: String]]? { + _externalIds + } + + public var dictionaryValue: [String: Any]? { + return self.dictionary + } + + enum CodingKeys: String, CodingKey { + case _app = "app" + case _device = "device" + case _library = "library" + case _os = "os" + case _screen = "screen" + case _locale = "locale" + case _network = "network" + case _timezone = "timezone" + case _traits = "traits" + case _externalIds = "externalId" + } + + static func locale() -> String { + if #available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) { + return "\(Locale.current.language.languageCode?.identifier ?? "")-\(Locale.current.region?.identifier ?? "")" + } else { + return "\(Locale.current.languageCode ?? "")-\(Locale.current.regionCode ?? "")" + } + } + + static func timezone() -> String { + return TimeZone.current.identifier + } + + static func traits(userDefaults: RSUserDefaults) -> JSON? { + let traitsJSON: JSON? = userDefaults.read(.traits) + var traitsDict = traitsJSON?.dictionaryValue + if let userId: String = userDefaults.read(.userId) { + traitsDict?["userId"] = userId + } + if let anonymousId: String = userDefaults.read(.anonymousId) { + traitsDict?["anonymousId"] = anonymousId + } + if let traits = traitsDict { + return try? JSON(traits) + } + return nil + } + + internal init(userDefaults: RSUserDefaults) { + _app = AppInfo() + _device = DeviceInfo() + _library = LibraryInfo() + _os = OSInfo() + _screen = ScreenInfo() + _locale = Self.locale() + _network = NetworkInfo() + _timezone = Self.timezone() + _traits = Self.traits(userDefaults: userDefaults) + _externalIds = userDefaults.read(.externalId) + } +} + +public extension Encodable { + var dictionary: [String: Any]? { + guard let data = try? JSONEncoder().encode(self) else { return nil } + return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] } + } +} diff --git a/Sources/Classes/Domain/Models/RSOption.swift b/Sources/Classes/Domain/Models/RSOption.swift index 3efb97fc..46dc6de3 100644 --- a/Sources/Classes/Domain/Models/RSOption.swift +++ b/Sources/Classes/Domain/Models/RSOption.swift @@ -23,11 +23,11 @@ open class RSOption: NSObject { @objc public func putExternalId(_ type: String, withId id: String) { guard type.isNotEmpty else { - RSClient.rsLog(message: "ExternalId type can not be empty", logLevel: .warning) + Logger.log(message: "ExternalId type can not be empty", logLevel: .warning) return } guard id.isNotEmpty else { - RSClient.rsLog(message: "External id can not be empty", logLevel: .warning) + Logger.log(message: "External id can not be empty", logLevel: .warning) return } if externalIds == nil { @@ -46,7 +46,7 @@ open class RSOption: NSObject { @objc public func putIntegration(_ type: String, isEnabled enabled: Bool) { guard type.isNotEmpty else { - RSClient.rsLog(message: "Integration type can not be empty", logLevel: .warning) + Logger.log(message: "Integration type can not be empty", logLevel: .warning) return } integrations?[type] = enabled @@ -55,7 +55,7 @@ open class RSOption: NSObject { @objc public func putCustomContext(_ context: [String: Any], withKey key: String) { guard key.isNotEmpty else { - RSClient.rsLog(message: "CustomContext key can not be empty", logLevel: .warning) + Logger.log(message: "CustomContext key can not be empty", logLevel: .warning) return } if customContexts == nil { diff --git a/Sources/Classes/Domain/Models/RSUserInfo.swift b/Sources/Classes/Domain/Models/RSUserInfo.swift new file mode 100644 index 00000000..9c93741a --- /dev/null +++ b/Sources/Classes/Domain/Models/RSUserInfo.swift @@ -0,0 +1,15 @@ +// +// RSUserInfo.swift +// Rudder +// +// Created by Pallab Maiti on 15/07/22. +// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import Foundation + +struct RSUserInfo { + var anonymousId: String? + var userId: String? + var traits: JSON? +} diff --git a/Sources/Classes/Domain/Protocols/RSConsoleLogger.swift b/Sources/Classes/Domain/Protocols/RSConsoleLogger.swift deleted file mode 100644 index deb87872..00000000 --- a/Sources/Classes/Domain/Protocols/RSConsoleLogger.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// RSConsoleLogger.swift -// RudderStack -// -// Created by Pallab Maiti on 24/02/22. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -class RSConsoleLogger: RSLogger { - func parseLog(_ log: RSLogMessage) { - var metadata = "" - if let function = log.function, let line = log.line { - metadata = " - \(function):\(line)" - } - print("\(TAG):\(log.logLevel.toString()):\(metadata):\(log.message)") - } -} diff --git a/Sources/Classes/Domain/Protocols/RSLogger.swift b/Sources/Classes/Domain/Protocols/RSLogger.swift deleted file mode 100644 index 2e8d3010..00000000 --- a/Sources/Classes/Domain/Protocols/RSLogger.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// RSLogger.swift -// RudderStack -// -// Created by Pallab Maiti on 24/02/22. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -public protocol RSLogger { - func parseLog(_ log: RSLogMessage) -} - -public struct RSLoggingType: Hashable { - - public enum LogDestination { - case log - case metric - } - - public static let log = RSLoggingType(types: [.log]) - public static let metric = RSLoggingType(types: [.metric]) - - init(types: [LogDestination]) { - self.allTypes = types - } - - private let allTypes: [LogDestination] - func contains(_ destination: LogDestination) -> Bool { - return allTypes.contains(destination) - } -} - -public protocol RSLogMessage { - var logLevel: RSLogLevel { get } - var title: String? { get } - var message: String { get } - var event: RSMessage? { get } - var function: String? { get } - var line: Int? { get } - var logType: RSLoggingType.LogDestination { get } - var dateTime: Date { get } -} - -public enum RSMetricType: Int { - case counter = 0 // Not Verbose - case gauge // Semi-verbose - - func toString() -> String { - var typeString = "Gauge" - if self == .counter { - typeString = "Counter" - } - return typeString - } - - static func fromString(_ string: String) -> Self { - var returnType = Self.counter - if string == "Gauge" { - returnType = .gauge - } - - return returnType - } -} - -// MARK: - Logging API - -extension RSClient { - - /// The logging method for capturing all general types of log messages related to RudderStack. - /// - Parameters: - /// - message: The main message of the log to be captured. - /// - logLevel: Usually .error, .warning or .debug, in order of serverity. This helps filter logs based on this added metadata. - /// - function: The name of the function the log came from. This will be captured automatically. - /// - line: The line number in the function the log came from. This will be captured automatically. - public func log(message: String, logLevel: RSLogLevel? = nil, function: String = #function, line: Int = #line) { - apply { plugin in - // Check if we should send off the event - if RSLoggerPlugin.loggingEnabled == false { - return - } - if let loggerPlugin = plugin as? RSLoggerPlugin { - var filterKind = loggerPlugin.logLevel - if let logKind = logLevel { - filterKind = logKind - } - - let log = LogFactory.buildLog(destination: .log, title: "", message: message, logLevel: filterKind, function: function, line: line) - loggerPlugin.log(log, destination: .log) - } - } - } - - /// The logging method for capturing metrics related to RudderStack or other libraries. - /// - Parameters: - /// - type: Metric type, usually .counter or .gauge. Select the one that makes sense for the metric. - /// - name: The title of the metric to track. - /// - value: The value associated with the metric. This would be an incrementing counter or time or pressure gauge. - /// - tags: Any tags that should be associated with the metric. Any extra metadata that may help. - public func metric(_ type: RSMetricType, name: String, value: Double, tags: [String]? = nil) { - apply { plugin in - // Check if we should send off the event - if RSLoggerPlugin.loggingEnabled == false { - return - } - - if let loggerPlugin = plugin as? RSLoggerPlugin { - - let log = LogFactory.buildLog(destination: .metric, title: type.toString(), message: name, value: value, tags: tags) - loggerPlugin.log(log, destination: .metric) - } - } - } -} - -extension RSClient { - /// Add a logging target to the system. These `targets` can handle logs in various ways. Consider - /// sending logs to the console, the OS and a web service. Three targets can handle these scenarios. - /// - Parameters: - /// - target: A `LogTarget` that has logic to parse and handle log messages. - /// - type: The type consists of `log`, `metric` or `history`. These correspond to the - /// public API on Analytics. - public func add(target: RSLogger, type: RSLoggingType) { - apply { (potentialLogger) in - if let logger = potentialLogger as? RSLoggerPlugin { - do { - try logger.add(target: target, for: type) - } catch { - Self.rsLog(message: "Could not add target: \(error.localizedDescription)", logLevel: .error) - } - } - } - } -} diff --git a/Sources/Classes/Domain/Protocols/RSMessage.swift b/Sources/Classes/Domain/Protocols/RSMessage.swift index 23d215b2..2b12be98 100644 --- a/Sources/Classes/Domain/Protocols/RSMessage.swift +++ b/Sources/Classes/Domain/Protocols/RSMessage.swift @@ -19,6 +19,8 @@ public protocol RSMessage { var option: RSOption? { get set } var channel: String? { get set } var dictionaryValue: [String: Any] { get } + var sessionId: Int? { get set } + var sessionStart: Bool? { get set } } public struct TrackMessage: RSMessage { @@ -31,6 +33,8 @@ public struct TrackMessage: RSMessage { public var integrations: MessageIntegrations? public var option: RSOption? public var channel: String? + public var sessionId: Int? + public var sessionStart: Bool? public let event: String public let properties: TrackProperties? @@ -63,6 +67,8 @@ public struct IdentifyMessage: RSMessage { public var integrations: MessageIntegrations? public var option: RSOption? public var channel: String? + public var sessionId: Int? + public var sessionStart: Bool? public var traits: IdentifyTraits? @@ -93,7 +99,9 @@ public struct ScreenMessage: RSMessage { public var integrations: MessageIntegrations? public var option: RSOption? public var channel: String? - + public var sessionId: Int? + public var sessionStart: Bool? + public let name: String public let category: String? public let properties: ScreenProperties? @@ -128,7 +136,9 @@ public struct GroupMessage: RSMessage { public var integrations: MessageIntegrations? public var option: RSOption? public var channel: String? - + public var sessionId: Int? + public var sessionStart: Bool? + public let groupId: String public let traits: GroupTraits? @@ -160,7 +170,9 @@ public struct AliasMessage: RSMessage { public var integrations: MessageIntegrations? public var option: RSOption? public var channel: String? - + public var sessionId: Int? + public var sessionStart: Bool? + public var previousId: String? public var dictionaryValue: [String: Any] { @@ -174,8 +186,9 @@ public struct AliasMessage: RSMessage { dictionary["previousId"] = previousId } - init(newId: String, option: RSOption? = nil) { + init(newId: String, previousId: String?, option: RSOption? = nil) { self.userId = newId + self.previousId = previousId self.option = option } } @@ -183,9 +196,34 @@ public struct AliasMessage: RSMessage { // MARK: - RawEvent data helpers extension RSMessage { - internal func applyRawEventData() -> Self { + internal func applyRawEventData(userInfo: RSUserInfo?) -> Self { var result: Self = self - result.messageId = String(format: "%ld-%@", RSUtils.getTimeStamp(), RSUtils.getUniqueId()) + result.context = MessageContext() + if let traits = userInfo?.traits?.dictionaryValue { + result.context?[keyPath: "traits"] = traits + } + if let userId = userInfo?.userId { + result.context?[keyPath: "traits.userId"] = userId + } + if let anonymousId = userInfo?.anonymousId { + result.context?[keyPath: "traits.anonymousId"] = anonymousId + } + /*var device = [String: Any]() + if let deviceToken: String = RSSessionStorage.shared.read(.deviceToken) { + device["token"] = deviceToken + } + if let advertisingId: String = RSSessionStorage.shared.read(.advertisingId), advertisingId.isNotEmpty { + device["advertisingId"] = advertisingId + device["adTrackingEnabled"] = true + let appTrackingConsent: RSAppTrackingConsent = RSSessionStorage.shared.read(.appTrackingConsent) ?? .notDetermined + device["attTrackingStatus"] = appTrackingConsent.rawValue + } + if !device.isEmpty { + result.context?["device"] = device + }*/ + result.userId = userInfo?.userId + result.anonymousId = userInfo?.anonymousId + result.messageId = RSUtils.getUniqueId() result.timestamp = RSUtils.getTimestampString() result.channel = "mobile" return result diff --git a/Sources/Classes/Helpers/Logger.swift b/Sources/Classes/Helpers/Logger.swift new file mode 100644 index 00000000..4e5c1053 --- /dev/null +++ b/Sources/Classes/Helpers/Logger.swift @@ -0,0 +1,68 @@ +// +// Logger.swift +// Rudder +// +// Created by Pallab Maiti on 27/11/23. +// Copyright © 2023 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import Foundation + +@frozen @objc public enum RSLogLevel: Int { + case verbose = 5 + case debug = 4 + case info = 3 + case warning = 2 + case error = 1 + case none = 0 + + public func toString() -> String { + switch self { + case .verbose: + return "Verbose" + case .debug: + return "Debug" + case .info: + return "Info" + case .warning: + return "Warning" + case .error: + return "Error" + case .none: + return "" + } + } +} + +class Logger { + static var logLevel: RSLogLevel = .error + + static func logDebug(_ message: String, function: String = #function, line: Int = #line) { + log(message: message, logLevel: .debug, function: function, line: line) + } + + static func logInfo(_ message: String, function: String = #function, line: Int = #line) { + log(message: message, logLevel: .info, function: function, line: line) + } + + static func logWarning(_ message: String, function: String = #function, line: Int = #line) { + log(message: message, logLevel: .warning, function: function, line: line) + } + + static func logError(_ message: String, function: String = #function, line: Int = #line) { + log(message: message, logLevel: .error, function: function, line: line) + } + + static func log(message: String, logLevel: RSLogLevel, function: String = #function, line: Int = #line) { + if self.logLevel == .verbose || self.logLevel == logLevel { + let metadata = " - \(function):\(line):" + print("RudderStack:\(logLevel.toString()):\(metadata)\(message)") + } + } +} + +extension RSClient { + public func log(message: String, logLevel: RSLogLevel) { + Logger.log(message: message, logLevel: logLevel) + } +} diff --git a/Sources/Classes/Helpers/Platforms/Mac/RSmacOSLifecycleEvents.swift b/Sources/Classes/Helpers/Platforms/Mac/RSmacOSLifecycleEvents.swift index 4fd727a9..d12d5270 100644 --- a/Sources/Classes/Helpers/Platforms/Mac/RSmacOSLifecycleEvents.swift +++ b/Sources/Classes/Helpers/Platforms/Mac/RSmacOSLifecycleEvents.swift @@ -11,13 +11,62 @@ import Foundation class RSmacOSLifecycleEvents: RSPlatformPlugin, RSmacOSLifecycle { let type = PluginType.before - var client: RSClient? + var client: RSClient? { + didSet { + initialSetup() + } + } @RSAtomic private var didFinishLaunching = false @RSAtomic private var fromBackground = false - + private var userDefaults: RSUserDefaults? + private var config: RSConfig? + + internal func initialSetup() { + guard let client = self.client else { return } + userDefaults = client.userDefaults + config = client.config + } + + func application(didFinishLaunchingWithOptions launchOptions: [String: Any]?) { + didFinishLaunching = true + + if config?.trackLifecycleEvents == false { + return + } + + let previousVersion: String? = userDefaults?.read(application: .version) + let previousBuild: String? = userDefaults?.read(application: .build) + + let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String + let currentBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String + + if previousVersion == nil { + client?.track("Application Installed", properties: RSUtils.getLifeCycleProperties( + currentVersion: currentVersion, + currentBuild: currentBuild + )) + } else if currentVersion != previousVersion { + client?.track("Application Updated", properties: RSUtils.getLifeCycleProperties( + previousVersion: previousVersion, + previousBuild: previousBuild, + currentVersion: currentVersion, + currentBuild: currentBuild + )) + } + + client?.track("Application Opened", properties: RSUtils.getLifeCycleProperties( + currentVersion: currentVersion, + currentBuild: currentBuild, + fromBackground: false + )) + + userDefaults?.write(application: .version, value: currentVersion) + userDefaults?.write(application: .build, value: currentBuild) + } + func applicationDidBecomeActive() { - if client?.config?.trackLifecycleEvents == false { + if config?.trackLifecycleEvents == false { return } @@ -27,8 +76,8 @@ class RSmacOSLifecycleEvents: RSPlatformPlugin, RSmacOSLifecycle { if didFinishLaunching == false { didFinishLaunching = true - let previousVersion = RSUserDefaults.getApplicationVersion() - let previousBuild = RSUserDefaults.getApplicationBuild() + let previousVersion: String? = userDefaults?.read(application: .version) + let previousBuild: String? = userDefaults?.read(application: .build) if previousVersion == nil { client?.track("Application Installed", properties: RSUtils.getLifeCycleProperties( @@ -44,8 +93,8 @@ class RSmacOSLifecycleEvents: RSPlatformPlugin, RSmacOSLifecycle { )) } - RSUserDefaults.saveApplicationVersion(currentVersion) - RSUserDefaults.saveApplicationBuild(currentBuild) + userDefaults?.write(application: .version, value: currentVersion) + userDefaults?.write(application: .build, value: currentBuild) } if fromBackground { @@ -71,7 +120,7 @@ class RSmacOSLifecycleEvents: RSPlatformPlugin, RSmacOSLifecycle { } func applicationWillTerminate() { - if client?.config?.trackLifecycleEvents == false { + if config?.trackLifecycleEvents == false { return } diff --git a/Sources/Classes/Helpers/Platforms/Mac/RSmacOSScreenViewEvents.swift b/Sources/Classes/Helpers/Platforms/Mac/RSmacOSScreenViewEvents.swift index eb327e06..ed139821 100644 --- a/Sources/Classes/Helpers/Platforms/Mac/RSmacOSScreenViewEvents.swift +++ b/Sources/Classes/Helpers/Platforms/Mac/RSmacOSScreenViewEvents.swift @@ -27,6 +27,8 @@ class RSmacOSScreenViewEvents: RSPlatformPlugin { } extension NSViewController { + static var client: RSClient? + static func rudderSwizzleView() { let originalSelector = #selector(self.viewDidAppear) let swizzledSelector = #selector(self.rsViewDidAppear) @@ -45,27 +47,10 @@ extension NSViewController { func rsViewDidAppear() { var name = NSStringFromClass(type(of: self)) name = name.replacingOccurrences(of: "ViewController", with: "") - let screenMessage = ScreenMessage(title: name, properties: ["automatic": true, "name": name]) + let screenMessage = ScreenMessage(title: name, properties: ["automatic": true, "name": name]).applyRawEventData(userInfo: NSViewController.client?.userInfo) NSViewController.client?.process(message: screenMessage) rsViewDidAppear() } } -extension NSViewController { - private struct AssociatedKey { - static var client: RSClient? - } - - static var client: RSClient? { - get { - return objc_getAssociatedObject(self, &AssociatedKey.client) as? RSClient - } - set { - if let value = newValue { - objc_setAssociatedObject(self, &AssociatedKey.client, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - } -} - #endif diff --git a/Sources/Classes/Helpers/Platforms/Vendors/AppleUtils.swift b/Sources/Classes/Helpers/Platforms/Vendors/AppleUtils.swift index ae15a9e9..74c39ac3 100644 --- a/Sources/Classes/Helpers/Platforms/Vendors/AppleUtils.swift +++ b/Sources/Classes/Helpers/Platforms/Vendors/AppleUtils.swift @@ -28,15 +28,19 @@ internal class PhoneVendor: Vendor { } override var type: String { -#if os(iOS) - return "ios" -#elseif os(tvOS) - return "tvos" -#elseif targetEnvironment(macCatalyst) - return "macos" -#else + #if os(iOS) + if UIDevice.current.userInterfaceIdiom == .pad { + return "iPadOS" + } else { + return "iOS" + } + #elseif os(tvOS) + return "tvOS" + #elseif targetEnvironment(macCatalyst) + return "macOS" + #else return "unknown" -#endif + #endif } override var model: String { @@ -46,11 +50,11 @@ internal class PhoneVendor: Vendor { override var name: String { // eg. "iPod Touch" - return device.model + return device.name } override var identifierForVendor: String? { - return device.identifierForVendor?.uuidString + return device.identifierForVendor?.uuidString.lowercased() } override var systemName: String { @@ -71,12 +75,12 @@ internal class PhoneVendor: Vendor { } override var requiredPlugins: [RSPlatformPlugin] { - return [RSiOSLifecycleMonitor(), RSDeviceTokenPlugin()] + return [RSiOSLifecycleMonitor()] } override var carrier: String { #if os(iOS) - return retrieveCarrierNames() ?? "unavailable"; + return retrieveCarrierNames() ?? "unavailable" #else return "unavailable" #endif @@ -85,34 +89,23 @@ internal class PhoneVendor: Vendor { #if os(iOS) func retrieveCarrierNames() -> String? { - let systemVersion = UIDevice.current.systemVersion - let versionComponents = systemVersion.split(separator: ".").compactMap { Int($0) } - if versionComponents.count > 0 { - let majorVersion = versionComponents[0] + if #available(iOS 16, *) { + Logger.log(message: "Unable to retrieve carrier name as the iOS version is >= 16", logLevel: .warning) + return nil + } else { + let networkInfo = CTTelephonyNetworkInfo() + var carrierNames: [String] = [] - if majorVersion >= 16 { - RSClient.rsLog(message: "Unable to retrieve carrier name as the iOS version is >= 16", logLevel: .warning) - return nil - } else if majorVersion >= 12 && majorVersion < 16 { - let networkInfo = CTTelephonyNetworkInfo() - var carrierNames: [String] = [] - - if let carriers = networkInfo.serviceSubscriberCellularProviders?.values { - for carrierObj in carriers { - if let carrierName = carrierObj.carrierName, carrierName != "--" { - carrierNames.append(carrierName) - } + if let carriers = networkInfo.serviceSubscriberCellularProviders?.values { + for carrierObj in carriers { + if let carrierName = carrierObj.carrierName, carrierName != "--" { + carrierNames.append(carrierName) } } - if(!carrierNames.isEmpty) { - let formattedCarrierNames = carrierNames.joined(separator: ", ") - return formattedCarrierNames - } - } else { - let networkInfo = CTTelephonyNetworkInfo() - if let carrier = networkInfo.subscriberCellularProvider?.carrierName, carrier != "--" { - return carrier - } + } + if !carrierNames.isEmpty { + let formattedCarrierNames = carrierNames.joined(separator: ", ") + return formattedCarrierNames } } return nil @@ -147,7 +140,7 @@ internal class WatchVendor: Vendor { } override var type: String { - return "watchos" + return "watchOS" } override var model: String { @@ -155,11 +148,11 @@ internal class WatchVendor: Vendor { } override var name: String { - return device.model + return device.name } override var identifierForVendor: String? { - return device.identifierForVendor?.uuidString + return device.identifierForVendor?.uuidString.lowercased() } override var systemName: String { @@ -229,7 +222,7 @@ internal class MacVendor: Vendor { } override var type: String { - return "macos" + return "macOS" } override var model: String { @@ -241,9 +234,7 @@ internal class MacVendor: Vendor { } override var identifierForVendor: String? { - // apple suggested to use this for receipt validation - // in MAS, works for this too. - return macAddress(bsd: "en0") + return macAddress(bsd: "en0")?.lowercased() } override var systemName: String { @@ -267,7 +258,7 @@ internal class MacVendor: Vendor { } override var requiredPlugins: [RSPlatformPlugin] { - return [RSmacOSLifecycleMonitor(), RSDeviceTokenPlugin()] + return [RSmacOSLifecycleMonitor()] } private func deviceModel() -> String { diff --git a/Sources/Classes/Helpers/Platforms/iOS/RSiOSLifecycleEvents.swift b/Sources/Classes/Helpers/Platforms/iOS/RSiOSLifecycleEvents.swift index ffa664aa..5a74e1ae 100644 --- a/Sources/Classes/Helpers/Platforms/iOS/RSiOSLifecycleEvents.swift +++ b/Sources/Classes/Helpers/Platforms/iOS/RSiOSLifecycleEvents.swift @@ -14,20 +14,32 @@ import UIKit class RSiOSLifecycleEvents: RSPlatformPlugin, RSiOSLifecycle { let type = PluginType.before - var client: RSClient? - var isFirstTimeLaunch: Bool = true + var client: RSClient? { + didSet { + initialSetup() + } + } + var isFirstTimeLaunch: Bool = true @RSAtomic private var didFinishLaunching = false + private var userDefaults: RSUserDefaults? + private var config: RSConfig? + + internal func initialSetup() { + guard let client = self.client else { return } + userDefaults = client.userDefaults + config = client.config + } func application(_ application: UIApplication?, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { didFinishLaunching = true - if client?.config?.trackLifecycleEvents == false { + if config?.trackLifecycleEvents == false { return } - let previousVersion = RSUserDefaults.getApplicationVersion() - let previousBuild = RSUserDefaults.getApplicationBuild() + let previousVersion: String? = userDefaults?.read(application: .version) + let previousBuild: String? = userDefaults?.read(application: .build) let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String let currentBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String @@ -54,12 +66,12 @@ class RSiOSLifecycleEvents: RSPlatformPlugin, RSiOSLifecycle { url: launchOptions?[UIApplication.LaunchOptionsKey.url] )) - RSUserDefaults.saveApplicationVersion(currentVersion) - RSUserDefaults.saveApplicationBuild(currentBuild) + userDefaults?.write(application: .version, value: currentVersion) + userDefaults?.write(application: .build, value: currentBuild) } func applicationWillEnterForeground(application: UIApplication?) { - if client?.config?.trackLifecycleEvents == false { + if config?.trackLifecycleEvents == false { return } #if !os(tvOS) @@ -83,7 +95,7 @@ class RSiOSLifecycleEvents: RSPlatformPlugin, RSiOSLifecycle { } func applicationDidEnterBackground(application: UIApplication?) { - if client?.config?.trackLifecycleEvents == false { + if config?.trackLifecycleEvents == false { return } @@ -91,7 +103,7 @@ class RSiOSLifecycleEvents: RSPlatformPlugin, RSiOSLifecycle { } func applicationDidBecomeActive(application: UIApplication?) { - if client?.config?.trackLifecycleEvents == false { + if config?.trackLifecycleEvents == false { return } diff --git a/Sources/Classes/Helpers/Platforms/iOS/RSiOSLifecycleMonitor.swift b/Sources/Classes/Helpers/Platforms/iOS/RSiOSLifecycleMonitor.swift index 3a5c6d83..b5b71463 100644 --- a/Sources/Classes/Helpers/Platforms/iOS/RSiOSLifecycleMonitor.swift +++ b/Sources/Classes/Helpers/Platforms/iOS/RSiOSLifecycleMonitor.swift @@ -13,7 +13,7 @@ import UIKit class RSiOSLifecycleMonitor: RSPlatformPlugin { let type = PluginType.utility - var client: RSClient? + weak var client: RSClient? private var application: UIApplication = UIApplication.shared private var appNotifications: [NSNotification.Name] = [UIApplication.didEnterBackgroundNotification, @@ -35,23 +35,23 @@ class RSiOSLifecycleMonitor: RSPlatformPlugin { func notificationResponse(notification: NSNotification) { switch notification.name { case UIApplication.didEnterBackgroundNotification: - self.didEnterBackground(notification: notification) + didEnterBackground(notification: notification) case UIApplication.willEnterForegroundNotification: - self.applicationWillEnterForeground(notification: notification) + applicationWillEnterForeground(notification: notification) case UIApplication.didFinishLaunchingNotification: - self.didFinishLaunching(notification: notification) + didFinishLaunching(notification: notification) case UIApplication.didBecomeActiveNotification: - self.didBecomeActive(notification: notification) + didBecomeActive(notification: notification) case UIApplication.willResignActiveNotification: - self.willResignActive(notification: notification) + willResignActive(notification: notification) case UIApplication.didReceiveMemoryWarningNotification: - self.didReceiveMemoryWarning(notification: notification) + didReceiveMemoryWarning(notification: notification) case UIApplication.significantTimeChangeNotification: - self.significantTimeChange(notification: notification) + significantTimeChange(notification: notification) case UIApplication.backgroundRefreshStatusDidChangeNotification: - self.backgroundRefreshDidChange(notification: notification) + backgroundRefreshDidChange(notification: notification) case UIApplication.willTerminateNotification: - self.willTerminate(notification: notification) + willTerminate(notification: notification) default: break diff --git a/Sources/Classes/Helpers/Platforms/iOS/RSiOSScreenViewEvents.swift b/Sources/Classes/Helpers/Platforms/iOS/RSiOSScreenViewEvents.swift index 4877c729..762c6cfe 100644 --- a/Sources/Classes/Helpers/Platforms/iOS/RSiOSScreenViewEvents.swift +++ b/Sources/Classes/Helpers/Platforms/iOS/RSiOSScreenViewEvents.swift @@ -26,6 +26,8 @@ class RSiOSScreenViewEvents: RSPlatformPlugin { } extension UIViewController { + static var client: RSClient? + static func rudderSwizzleView() { let originalSelector = #selector(viewDidAppear(_:)) let swizzledSelector = #selector(rsViewDidAppear(_:)) @@ -44,27 +46,10 @@ extension UIViewController { func rsViewDidAppear(_ animated: Bool) { var name = NSStringFromClass(type(of: self)) name = name.replacingOccurrences(of: "ViewController", with: "") - let screenMessage = ScreenMessage(title: name, properties: ["automatic": true, "name": name]) + let screenMessage = ScreenMessage(title: name, properties: ["automatic": true, "name": name]).applyRawEventData(userInfo: UIViewController.client?.userInfo) UIViewController.client?.process(message: screenMessage) rsViewDidAppear(animated) } } -extension UIViewController { - private struct AssociatedKey { - static var client: RSClient? - } - - static var client: RSClient? { - get { - return objc_getAssociatedObject(self, &AssociatedKey.client) as? RSClient - } - set { - if let value = newValue { - objc_setAssociatedObject(self, &AssociatedKey.client, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - } -} - #endif diff --git a/Sources/Classes/Helpers/Platforms/watchOS/RSwatchOSLifecycleEvents.swift b/Sources/Classes/Helpers/Platforms/watchOS/RSwatchOSLifecycleEvents.swift index 56e6fc8f..95d2f7fe 100644 --- a/Sources/Classes/Helpers/Platforms/watchOS/RSwatchOSLifecycleEvents.swift +++ b/Sources/Classes/Helpers/Platforms/watchOS/RSwatchOSLifecycleEvents.swift @@ -13,15 +13,28 @@ import WatchKit class RSwatchOSLifecycleEvents: RSPlatformPlugin, RSwatchOSLifecycle { let type = PluginType.before - var client: RSClient? + var client: RSClient? { + didSet { + initialSetup() + } + } + + private var userDefaults: RSUserDefaults? + private var config: RSConfig? + + internal func initialSetup() { + guard let client = self.client else { return } + userDefaults = client.userDefaults + config = client.config + } func applicationDidFinishLaunching(watchExtension: WKExtension?) { - if client?.config?.trackLifecycleEvents == false { + if config?.trackLifecycleEvents == false { return } - let previousVersion = RSUserDefaults.getApplicationVersion() - let previousBuild = RSUserDefaults.getApplicationBuild() + let previousVersion: String? = userDefaults?.read(application: .version) + let previousBuild: String? = userDefaults?.read(application: .build) let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String let currentBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String @@ -46,12 +59,12 @@ class RSwatchOSLifecycleEvents: RSPlatformPlugin, RSwatchOSLifecycle { fromBackground: false )) - RSUserDefaults.saveApplicationVersion(currentVersion) - RSUserDefaults.saveApplicationBuild(currentBuild) + userDefaults?.write(application: .version, value: currentVersion) + userDefaults?.write(application: .build, value: currentBuild) } func applicationWillEnterForeground(watchExtension: WKExtension?) { - if client?.config?.trackLifecycleEvents == false { + if config?.trackLifecycleEvents == false { return } diff --git a/Sources/Classes/Helpers/Platforms/watchOS/RSwatchOSScreenViewEvents.swift b/Sources/Classes/Helpers/Platforms/watchOS/RSwatchOSScreenViewEvents.swift index 88f1ccee..19897d4d 100644 --- a/Sources/Classes/Helpers/Platforms/watchOS/RSwatchOSScreenViewEvents.swift +++ b/Sources/Classes/Helpers/Platforms/watchOS/RSwatchOSScreenViewEvents.swift @@ -25,6 +25,8 @@ class RSwatchOSScreenViewEvents: RSPlatformPlugin { } } extension WKInterfaceController { + static var client: RSClient? + static func rudderSwizzleView() { let originalSelector = #selector(didAppear) let swizzledSelector = #selector(rsDidAppear) @@ -43,27 +45,10 @@ extension WKInterfaceController { func rsDidAppear() { var name = NSStringFromClass(Swift.type(of: self)) name = name.replacingOccurrences(of: "InterfaceController", with: "") - let screenMessage = ScreenMessage(title: name, properties: ["automatic": true, "name": name]) + let screenMessage = ScreenMessage(title: name, properties: ["automatic": true, "name": name]).applyRawEventData(userInfo: WKInterfaceController.client?.userInfo) WKInterfaceController.client?.process(message: screenMessage) rsDidAppear() } } -extension WKInterfaceController { - private struct AssociatedKey { - static var client: RSClient? - } - - static var client: RSClient? { - get { - return objc_getAssociatedObject(self, &AssociatedKey.client) as? RSClient - } - set { - if let value = newValue { - objc_setAssociatedObject(self, &AssociatedKey.client, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - } -} - #endif diff --git a/Sources/Classes/Helpers/RSAnyCodable.swift b/Sources/Classes/Helpers/RSAnyCodable.swift deleted file mode 100644 index 04e269fc..00000000 --- a/Sources/Classes/Helpers/RSAnyCodable.swift +++ /dev/null @@ -1,137 +0,0 @@ -// -// RSAnyCodable.swift -// RudderStack -// -// Created by Pallab Maiti on 16/11/21. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import Foundation - -// swiftlint:disable cyclomatic_complexity - -@frozen public struct RSAnyCodable: Codable { - public let value: Any - - public init(_ value: T?) { - self.value = value ?? () - } -} - -extension RSAnyCodable: _RSAnyEncodable, _RSAnyDecodable {} - -extension RSAnyCodable: Equatable { - public static func == (lhs: RSAnyCodable, rhs: RSAnyCodable) -> Bool { - switch (lhs.value, rhs.value) { - case is (Void, Void): - return true - case let (lhs as Bool, rhs as Bool): - return lhs == rhs - case let (lhs as Int, rhs as Int): - return lhs == rhs - case let (lhs as Int8, rhs as Int8): - return lhs == rhs - case let (lhs as Int16, rhs as Int16): - return lhs == rhs - case let (lhs as Int32, rhs as Int32): - return lhs == rhs - case let (lhs as Int64, rhs as Int64): - return lhs == rhs - case let (lhs as UInt, rhs as UInt): - return lhs == rhs - case let (lhs as UInt8, rhs as UInt8): - return lhs == rhs - case let (lhs as UInt16, rhs as UInt16): - return lhs == rhs - case let (lhs as UInt32, rhs as UInt32): - return lhs == rhs - case let (lhs as UInt64, rhs as UInt64): - return lhs == rhs - case let (lhs as Float, rhs as Float): - return lhs == rhs - case let (lhs as Double, rhs as Double): - return lhs == rhs - case let (lhs as String, rhs as String): - return lhs == rhs - case let (lhs as [String: RSAnyCodable], rhs as [String: RSAnyCodable]): - return lhs == rhs - case let (lhs as [RSAnyCodable], rhs as [RSAnyCodable]): - return lhs == rhs - default: - return false - } - } -} - -extension RSAnyCodable: CustomStringConvertible { - public var description: String { - switch value { - case is Void: - return String(describing: nil as Any?) - case let value as CustomStringConvertible: - return value.description - default: - return String(describing: value) - } - } -} - -extension RSAnyCodable: CustomDebugStringConvertible { - public var debugDescription: String { - switch value { - case let value as CustomDebugStringConvertible: - return "AnyCodable(\(value.debugDescription))" - default: - return "AnyCodable(\(description))" - } - } -} - -extension RSAnyCodable: ExpressibleByNilLiteral {} -extension RSAnyCodable: ExpressibleByBooleanLiteral {} -extension RSAnyCodable: ExpressibleByIntegerLiteral {} -extension RSAnyCodable: ExpressibleByFloatLiteral {} -extension RSAnyCodable: ExpressibleByStringLiteral {} -extension RSAnyCodable: ExpressibleByArrayLiteral {} -extension RSAnyCodable: ExpressibleByDictionaryLiteral {} - -extension RSAnyCodable: Hashable { - public func hash(into hasher: inout Hasher) { - switch value { - case let value as Bool: - hasher.combine(value) - case let value as Int: - hasher.combine(value) - case let value as Int8: - hasher.combine(value) - case let value as Int16: - hasher.combine(value) - case let value as Int32: - hasher.combine(value) - case let value as Int64: - hasher.combine(value) - case let value as UInt: - hasher.combine(value) - case let value as UInt8: - hasher.combine(value) - case let value as UInt16: - hasher.combine(value) - case let value as UInt32: - hasher.combine(value) - case let value as UInt64: - hasher.combine(value) - case let value as Float: - hasher.combine(value) - case let value as Double: - hasher.combine(value) - case let value as String: - hasher.combine(value) - case let value as [String: RSAnyCodable]: - hasher.combine(value) - case let value as [RSAnyCodable]: - hasher.combine(value) - default: - break - } - } -} diff --git a/Sources/Classes/Helpers/RSAnyDecodable.swift b/Sources/Classes/Helpers/RSAnyDecodable.swift deleted file mode 100644 index 31ff5d09..00000000 --- a/Sources/Classes/Helpers/RSAnyDecodable.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// RSAnyDecodable.swift -// RudderStack -// -// Created by Pallab Maiti on 17/11/21. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -#if canImport(Foundation) -import Foundation -#endif - -// swiftlint:disable cyclomatic_complexity -// swiftlint:disable type_name - -@frozen public struct RSAnyDecodable: Decodable { - public let value: Any - - public init(_ value: T?) { - self.value = value ?? () - } -} - -@usableFromInline -protocol _RSAnyDecodable { - var value: Any { get } - init(_ value: T?) -} - -extension RSAnyDecodable: _RSAnyDecodable {} - -extension _RSAnyDecodable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - - if container.decodeNil() { - #if canImport(Foundation) - self.init(NSNull()) - #else - self.init(self?) - #endif - } else if let bool = try? container.decode(Bool.self) { - self.init(bool) - } else if let int = try? container.decode(Int.self) { - self.init(int) - } else if let uint = try? container.decode(UInt.self) { - self.init(uint) - } else if let double = try? container.decode(Double.self) { - self.init(double) - } else if let string = try? container.decode(String.self) { - self.init(string) - } else if let array = try? container.decode([RSAnyDecodable].self) { - self.init(array.map { $0.value }) - } else if let dictionary = try? container.decode([String: RSAnyDecodable].self) { - self.init(dictionary.mapValues { $0.value }) - } else { - throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyDecodable value cannot be decoded") - } - } -} - -extension RSAnyDecodable: Equatable { - public static func == (lhs: RSAnyDecodable, rhs: RSAnyDecodable) -> Bool { - switch (lhs.value, rhs.value) { -#if canImport(Foundation) - case is (NSNull, NSNull), is (Void, Void): - return true -#endif - case let (lhs as Bool, rhs as Bool): - return lhs == rhs - case let (lhs as Int, rhs as Int): - return lhs == rhs - case let (lhs as Int8, rhs as Int8): - return lhs == rhs - case let (lhs as Int16, rhs as Int16): - return lhs == rhs - case let (lhs as Int32, rhs as Int32): - return lhs == rhs - case let (lhs as Int64, rhs as Int64): - return lhs == rhs - case let (lhs as UInt, rhs as UInt): - return lhs == rhs - case let (lhs as UInt8, rhs as UInt8): - return lhs == rhs - case let (lhs as UInt16, rhs as UInt16): - return lhs == rhs - case let (lhs as UInt32, rhs as UInt32): - return lhs == rhs - case let (lhs as UInt64, rhs as UInt64): - return lhs == rhs - case let (lhs as Float, rhs as Float): - return lhs == rhs - case let (lhs as Double, rhs as Double): - return lhs == rhs - case let (lhs as String, rhs as String): - return lhs == rhs - case let (lhs as [String: RSAnyDecodable], rhs as [String: RSAnyDecodable]): - return lhs == rhs - case let (lhs as [RSAnyDecodable], rhs as [RSAnyDecodable]): - return lhs == rhs - default: - return false - } - } -} - -extension RSAnyDecodable: CustomStringConvertible { - public var description: String { - switch value { - case is Void: - return String(describing: nil as Any?) - case let value as CustomStringConvertible: - return value.description - default: - return String(describing: value) - } - } -} - -extension RSAnyDecodable: CustomDebugStringConvertible { - public var debugDescription: String { - switch value { - case let value as CustomDebugStringConvertible: - return "AnyDecodable(\(value.debugDescription))" - default: - return "AnyDecodable(\(description))" - } - } -} - -extension RSAnyDecodable: Hashable { - public func hash(into hasher: inout Hasher) { - switch value { - case let value as Bool: - hasher.combine(value) - case let value as Int: - hasher.combine(value) - case let value as Int8: - hasher.combine(value) - case let value as Int16: - hasher.combine(value) - case let value as Int32: - hasher.combine(value) - case let value as Int64: - hasher.combine(value) - case let value as UInt: - hasher.combine(value) - case let value as UInt8: - hasher.combine(value) - case let value as UInt16: - hasher.combine(value) - case let value as UInt32: - hasher.combine(value) - case let value as UInt64: - hasher.combine(value) - case let value as Float: - hasher.combine(value) - case let value as Double: - hasher.combine(value) - case let value as String: - hasher.combine(value) - case let value as [String: RSAnyDecodable]: - hasher.combine(value) - case let value as [RSAnyDecodable]: - hasher.combine(value) - default: - break - } - } -} diff --git a/Sources/Classes/Helpers/RSAnyEncodable.swift b/Sources/Classes/Helpers/RSAnyEncodable.swift deleted file mode 100644 index 2fc41790..00000000 --- a/Sources/Classes/Helpers/RSAnyEncodable.swift +++ /dev/null @@ -1,275 +0,0 @@ -// -// RSAnyEncodable.swift -// RudderStack -// -// Created by Pallab Maiti on 17/11/21. -// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. -// - -#if canImport(Foundation) -import Foundation -#endif - -// swiftlint:disable cyclomatic_complexity -// swiftlint:disable function_body_length -// swiftlint:disable type_name - -@frozen public struct RSAnyEncodable: Encodable { - public let value: Any - - public init(_ value: T?) { - self.value = value ?? () - } -} - -@usableFromInline -protocol _RSAnyEncodable { - var value: Any { get } - init(_ value: T?) -} - -extension RSAnyEncodable: _RSAnyEncodable {} - -// MARK: - Encodable -extension _RSAnyEncodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - switch value { - #if canImport(Foundation) - case let number as NSNumber: - try encode(nsnumber: number, into: &container) - case is NSNull: - try container.encodeNil() - #endif - case is Void: - try container.encodeNil() - case let bool as Bool: - try container.encode(bool) - case let int as Int: - try container.encode(int) - case let int8 as Int8: - try container.encode(int8) - case let int16 as Int16: - try container.encode(int16) - case let int32 as Int32: - try container.encode(int32) - case let int64 as Int64: - try container.encode(int64) - case let uint as UInt: - try container.encode(uint) - case let uint8 as UInt8: - try container.encode(uint8) - case let uint16 as UInt16: - try container.encode(uint16) - case let uint32 as UInt32: - try container.encode(uint32) - case let uint64 as UInt64: - try container.encode(uint64) - case let float as Float: - try container.encode(float) - case let double as Double: - try container.encode(double) - case let string as String: - try container.encode(string) - #if canImport(Foundation) - case let date as Date: - try container.encode(date) - case let url as URL: - try container.encode(url) - #endif - case let array as [Any?]: - try container.encode(array.map { RSAnyEncodable($0) }) - case let dictionary as [String: Any?]: - try container.encode(dictionary.mapValues { RSAnyEncodable($0) }) - case let encodable as Encodable: - try encodable.encode(to: encoder) - default: - let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyEncodable value cannot be encoded") - throw EncodingError.invalidValue(value, context) - } - } - - #if canImport(Foundation) - private func encode(nsnumber: NSNumber, into container: inout SingleValueEncodingContainer) throws { - switch Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee))) { - case "c", "C": - try container.encode(nsnumber.boolValue) - case "s": - try container.encode(nsnumber.int8Value) - case "i": - try container.encode(nsnumber.int16Value) - case "l": - try container.encode(nsnumber.int32Value) - case "q": - try container.encode(nsnumber.int64Value) - case "S": - try container.encode(nsnumber.uint8Value) - case "I": - try container.encode(nsnumber.uint16Value) - case "L": - try container.encode(nsnumber.uint32Value) - case "Q": - try container.encode(nsnumber.uint64Value) - case "f": - try container.encode(nsnumber.floatValue) - case "d": - try container.encode(nsnumber.doubleValue) - default: - let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "NSNumber cannot be encoded because its type is not handled") - throw EncodingError.invalidValue(nsnumber, context) - } - } - #endif -} - -extension RSAnyEncodable: Equatable { - public static func == (lhs: RSAnyEncodable, rhs: RSAnyEncodable) -> Bool { - switch (lhs.value, rhs.value) { - case is (Void, Void): - return true - case let (lhs as Bool, rhs as Bool): - return lhs == rhs - case let (lhs as Int, rhs as Int): - return lhs == rhs - case let (lhs as Int8, rhs as Int8): - return lhs == rhs - case let (lhs as Int16, rhs as Int16): - return lhs == rhs - case let (lhs as Int32, rhs as Int32): - return lhs == rhs - case let (lhs as Int64, rhs as Int64): - return lhs == rhs - case let (lhs as UInt, rhs as UInt): - return lhs == rhs - case let (lhs as UInt8, rhs as UInt8): - return lhs == rhs - case let (lhs as UInt16, rhs as UInt16): - return lhs == rhs - case let (lhs as UInt32, rhs as UInt32): - return lhs == rhs - case let (lhs as UInt64, rhs as UInt64): - return lhs == rhs - case let (lhs as Float, rhs as Float): - return lhs == rhs - case let (lhs as Double, rhs as Double): - return lhs == rhs - case let (lhs as String, rhs as String): - return lhs == rhs - case let (lhs as [String: RSAnyEncodable], rhs as [String: RSAnyEncodable]): - return lhs == rhs - case let (lhs as [RSAnyEncodable], rhs as [RSAnyEncodable]): - return lhs == rhs - default: - return false - } - } -} - -extension RSAnyEncodable: CustomStringConvertible { - public var description: String { - switch value { - case is Void: - return String(describing: nil as Any?) - case let value as CustomStringConvertible: - return value.description - default: - return String(describing: value) - } - } -} - -extension RSAnyEncodable: CustomDebugStringConvertible { - public var debugDescription: String { - switch value { - case let value as CustomDebugStringConvertible: - return "AnyEncodable(\(value.debugDescription))" - default: - return "AnyEncodable(\(description))" - } - } -} - -extension RSAnyEncodable: ExpressibleByNilLiteral {} -extension RSAnyEncodable: ExpressibleByBooleanLiteral {} -extension RSAnyEncodable: ExpressibleByIntegerLiteral {} -extension RSAnyEncodable: ExpressibleByFloatLiteral {} -extension RSAnyEncodable: ExpressibleByStringLiteral {} -extension RSAnyEncodable: ExpressibleByStringInterpolation {} -extension RSAnyEncodable: ExpressibleByArrayLiteral {} -extension RSAnyEncodable: ExpressibleByDictionaryLiteral {} - -extension _RSAnyEncodable { - public init(nilLiteral _: ()) { - self.init(nil as Any?) - } - - public init(booleanLiteral value: Bool) { - self.init(value) - } - - public init(integerLiteral value: Int) { - self.init(value) - } - - public init(floatLiteral value: Double) { - self.init(value) - } - - public init(extendedGraphemeClusterLiteral value: String) { - self.init(value) - } - - public init(stringLiteral value: String) { - self.init(value) - } - - public init(arrayLiteral elements: Any...) { - self.init(elements) - } - - public init(dictionaryLiteral elements: (AnyHashable, Any)...) { - self.init([AnyHashable: Any](elements, uniquingKeysWith: { first, _ in first })) - } -} - -extension RSAnyEncodable: Hashable { - public func hash(into hasher: inout Hasher) { - switch value { - case let value as Bool: - hasher.combine(value) - case let value as Int: - hasher.combine(value) - case let value as Int8: - hasher.combine(value) - case let value as Int16: - hasher.combine(value) - case let value as Int32: - hasher.combine(value) - case let value as Int64: - hasher.combine(value) - case let value as UInt: - hasher.combine(value) - case let value as UInt8: - hasher.combine(value) - case let value as UInt16: - hasher.combine(value) - case let value as UInt32: - hasher.combine(value) - case let value as UInt64: - hasher.combine(value) - case let value as Float: - hasher.combine(value) - case let value as Double: - hasher.combine(value) - case let value as String: - hasher.combine(value) - case let value as [String: RSAnyEncodable]: - hasher.combine(value) - case let value as [RSAnyEncodable]: - hasher.combine(value) - default: - break - } - } -} diff --git a/Sources/Classes/Helpers/RSSessionStorage.swift b/Sources/Classes/Helpers/RSSessionStorage.swift new file mode 100644 index 00000000..6110bc18 --- /dev/null +++ b/Sources/Classes/Helpers/RSSessionStorage.swift @@ -0,0 +1,73 @@ +// +// RSSessionStorage.swift +// Rudder +// +// Created by Pallab Maiti on 16/07/22. +// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import Foundation + +class RSSessionStorage { + enum Keys: String { + case deviceToken + case advertisingId + case appTrackingConsent + case option + case context + } + + static let shared = RSSessionStorage() + private let syncQueue = DispatchQueue(label: "sessionStorage.rudder.com") + private var deviceToken: String? + private var advertisingId: String? + private var appTrackingConsent: RSAppTrackingConsent? + private var option: RSOption? + private var context: RSContext? + + func write(_ key: RSSessionStorage.Keys, value: T?) { + syncQueue.sync { + switch key { + case .deviceToken: + deviceToken = value as? String + case .advertisingId: + advertisingId = value as? String + case .appTrackingConsent: + appTrackingConsent = value as? RSAppTrackingConsent + case .option: + option = value as? RSOption + case .context: + if let value = value as? MessageContext { + if let data = try? JSONSerialization.data(withJSONObject: value) { + context = try? JSONDecoder().decode(RSContext.self, from: data) + } + } + } + } + } + + func read(_ key: RSSessionStorage.Keys) -> T? { + var result: T? + syncQueue.sync { + switch key { + case .deviceToken: + result = deviceToken as? T + case .advertisingId: + result = advertisingId as? T + case .appTrackingConsent: + result = appTrackingConsent as? T + case .option: + result = option as? T + case .context: + result = context as? T + } + } + return result + } + + func reset() { + syncQueue.sync { + + } + } +} diff --git a/Sources/Classes/Helpers/RSUserDefaults.swift b/Sources/Classes/Helpers/RSUserDefaults.swift new file mode 100644 index 00000000..74fe5129 --- /dev/null +++ b/Sources/Classes/Helpers/RSUserDefaults.swift @@ -0,0 +1,119 @@ +// +// RSUserDefaults.swift +// RudderStack +// +// Created by Pallab Maiti on 17/08/21. +// Copyright © 2021 Rudder Labs India Pvt Ltd. All rights reserved. +// + +import Foundation + +class RSUserDefaults { + enum Keys: String, CaseIterable { + case userId + case traits + case anonymousId + case serverConfig + case optStatus + case optInTime + case optOutTime + case externalId + case sessionId + case lastEventTimeStamp + case automaticSessionTrackingStatus + case manualSessionTrackingStatus + case sessionStoppedStatus + } + + enum ApplicationKeys: String { + case version + case build + } + + let syncQueue = DispatchQueue(label: "userDefaults.rudder.com") + let userDefaults: UserDefaults + + init(userDefaults: UserDefaults = UserDefaults.standard) { + self.userDefaults = userDefaults + } + + func write(_ key: RSUserDefaults.Keys, value: T?) { + syncQueue.sync { + if isBasicType(value: value) { + userDefaults.set(value, forKey: key.rawValue) + } else { + userDefaults.set(try? PropertyListEncoder().encode(value), forKey: key.rawValue) + } + userDefaults.synchronize() + } + } + + func read(_ key: RSUserDefaults.Keys) -> T? { + var result: T? + syncQueue.sync { + let raw = userDefaults.object(forKey: key.rawValue) + if let r = raw as? Data { + result = PropertyListDecoder().optionalDecode(T.self, from: r) + } else { + result = userDefaults.object(forKey: key.rawValue) as? T + } + } + return result + } + + func remove(_ key: RSUserDefaults.Keys) { + syncQueue.sync { + userDefaults.removeObject(forKey: key.rawValue) + userDefaults.synchronize() + } + } + + func write(application key: RSUserDefaults.ApplicationKeys, value: String?) { + syncQueue.sync { + userDefaults.set(value, forKey: key.rawValue) + userDefaults.synchronize() + } + } + + func read(application key: RSUserDefaults.ApplicationKeys) -> String? { + var result: String? + syncQueue.sync { + result = userDefaults.string(forKey: key.rawValue) + } + return result + } + + func reset() { + syncQueue.sync { + userDefaults.removeObject(forKey: RSUserDefaults.Keys.traits.rawValue) + userDefaults.removeObject(forKey: RSUserDefaults.Keys.externalId.rawValue) + userDefaults.removeObject(forKey: RSUserDefaults.Keys.userId.rawValue) + } + } +} + +extension RSUserDefaults { + func isBasicType(value: T?) -> Bool { + var result = false + if value == nil { + result = true + } else { + switch value { + case is NSNull, is Decimal, is NSNumber, is Bool, is String: + result = true + default: + break + } + } + return result + } +} + +extension PropertyListDecoder { + func optionalDecode(_ type: T.Type, from object: Any?) -> T? { + if let data = object as? Data { + return try? PropertyListDecoder().decode(T.self, from: data) + } + return nil + } +} diff --git a/Sources/Classes/Networking/APIClient/RSServiceManager.swift b/Sources/Classes/Networking/APIClient/RSServiceManager.swift index 50c746ea..197a089c 100644 --- a/Sources/Classes/Networking/APIClient/RSServiceManager.swift +++ b/Sources/Classes/Networking/APIClient/RSServiceManager.swift @@ -24,13 +24,15 @@ struct RSServiceManager: RSServiceType { return URLSession(configuration: configuration) }() + let urlSession: URLSession let client: RSClient var version: String { return "v1" } - init(client: RSClient) { + init(urlSession: URLSession = RSServiceManager.sharedSession, client: RSClient) { + self.urlSession = urlSession self.client = client } @@ -46,18 +48,18 @@ struct RSServiceManager: RSServiceType { extension RSServiceManager { func request(_ API: API, _ completion: @escaping Handler) { let urlString = [baseURL(API), path(API)].joined().addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) - client.log(message: "URL: \(urlString ?? "")", logLevel: .debug) + Logger.log(message: "URL: \(urlString ?? "")", logLevel: .debug) var request = URLRequest(url: URL(string: urlString ?? "")!) request.httpMethod = method(API).value if let headers = headers(API) { request.allHTTPHeaderFields = headers - client.log(message: "HTTPHeaderFields: \(headers)", logLevel: .debug) + Logger.log(message: "HTTPHeaderFields: \(headers)", logLevel: .debug) } if let httpBody = httpBody(API) { request.httpBody = httpBody - client.log(message: "HTTPBody: \(httpBody)", logLevel: .debug) + Logger.log(message: "HTTPBody: \(httpBody)", logLevel: .debug) } - let dataTask = RSServiceManager.sharedSession.dataTask(with: request, completionHandler: { (data, response, error) in + let dataTask = urlSession.dataTask(with: request, completionHandler: { (data, response, error) in if error != nil { completion(.failure(NSError(code: .SERVER_ERROR))) return @@ -73,7 +75,7 @@ extension RSServiceManager { default: do { if let data = data, let jsonString = String(data: data, encoding: .utf8) { - client.log(message: jsonString, logLevel: .debug) + Logger.log(message: jsonString, logLevel: .debug) } let object = try JSONDecoder().decode(T.self, from: data ?? Data()) completion(.success(object)) @@ -82,7 +84,7 @@ extension RSServiceManager { } } default: - let errorCode = handleCustomError(data: data ?? Data()) + let errorCode = handleCustomError(data: data ?? Data(), statusCode: statusCode) completion(.failure(NSError(code: errorCode))) } } else { @@ -92,17 +94,24 @@ extension RSServiceManager { dataTask.resume() } - func handleCustomError(data: Data) -> RSErrorCode { - do { - guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] else { + func handleCustomError(data: Data, statusCode: Int) -> RSErrorCode { + switch statusCode { + case 404: + return .RESOURCE_NOT_FOUND + case 400: + return .BAD_REQUEST + default: + do { + guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] else { + return .SERVER_ERROR + } + if let message = json["message"], message.lowercased() == "invalid write key" { + return .WRONG_WRITE_KEY + } + return .SERVER_ERROR + } catch { return .SERVER_ERROR } - if let message = json["message"], message == "Invalid write key" { - return .WRONG_WRITE_KEY - } - return .SERVER_ERROR - } catch { - return .SERVER_ERROR } } } @@ -113,9 +122,7 @@ extension RSServiceManager { "Authorization": "Basic \(client.config?.writeKey.computeAuthToken() ?? "")"] switch API { case .flushEvents: - if let anonymousIdPlugin = client.find(pluginType: RSAnonymousIdPlugin.self) { - headers["AnonymousId"] = anonymousIdPlugin.anonymousId?.computeAnonymousIdToken() ?? "" - } + headers["AnonymousId"] = client.anonymousId ?? "" default: break } @@ -125,12 +132,12 @@ extension RSServiceManager { func baseURL(_ API: API) -> String { switch API { case .flushEvents: - return "\(client.config?.dataPlaneUrl ?? RSDataPlaneUrl)/\(version)/" + return "\(client.config?.dataPlaneUrl ?? DEFAULT_DATA_PLANE_URL)/\(version)/" case .downloadConfig: if client.config?.controlPlaneUrl.hasSuffix("/") == true { - return "\(client.config?.controlPlaneUrl ?? RSControlPlaneUrl)" + return "\(client.config?.controlPlaneUrl ?? DEFAULT_CONTROL_PLANE_URL)" } else { - return "\(client.config?.controlPlaneUrl ?? RSControlPlaneUrl)/" + return "\(client.config?.controlPlaneUrl ?? DEFAULT_CONTROL_PLANE_URL)/" } } } diff --git a/Sources/Classes/Utility/Fixtures/ServerConfig.json b/Sources/Classes/Utility/Fixtures/ServerConfig.json new file mode 100644 index 00000000..9e2f3173 --- /dev/null +++ b/Sources/Classes/Utility/Fixtures/ServerConfig.json @@ -0,0 +1 @@ +{"source":{"id":"2PuQRyVWk5fmstevuIYS9tTlQos","name":"iOS Dev","writeKey":"2PuQRzr0QTU3cXSE880zk8pyv9I","config":{"statsCollection":{"errors":{"enabled":true},"metrics":{"enabled":true}}},"enabled":true,"workspaceId":"2CYviwoPKBATiXGmWYNpK5p2lPL","sourceDefinitionId":"1Q8kZmXcQ89PWXmdBlN3CSgkaO0","destinations":[{"id":"2Ku8x3PujDxvraFCvXeidOwNl4K","name":"Firebase Dev","enabled":true,"config":{"blacklistedEvents":[],"whitelistedEvents":[],"eventFilteringOption":"disable","oneTrustCookieCategories":[{"oneTrustCookieCategory":"Functional Cookies"}]},"liveEventsConfig":{},"destinationDefinitionId":"1YL4ZafH4z7LQkGDp0ThEwF7N1l","destinationDefinition":{"id":"1YL4ZafH4z7LQkGDp0ThEwF7N1l","name":"FIREBASE","displayName":"Firebase","createdAt":"2020-02-26T09:16:36.149Z","updatedAt":"2023-03-27T11:40:32.471Z","config":{"destConfig":{"ios":["useNativeSDK"],"unity":["useNativeSDK"],"android":["useNativeSDK"],"flutter":["useNativeSDK"],"reactnative":["useNativeSDK"],"defaultConfig":["blacklistedEvents","whitelistedEvents","eventFilteringOption","oneTrustCookieCategories"]},"secretKeys":[],"excludeKeys":[],"includeKeys":["blacklistedEvents","whitelistedEvents","eventFilteringOption","oneTrustCookieCategories"],"transformAt":"processor","transformAtV1":"processor","supportedSourceTypes":["android","ios","unity","reactnative","flutter"],"saveDestinationResponse":false},"responseRules":null,"category":null,"options":null},"deleted":false,"createdAt":"2023-01-27T09:55:50.082Z","updatedAt":"2023-08-04T07:42:33.295Z","workspaceId":"2CYviwoPKBATiXGmWYNpK5p2lPL","revisionId":"2TVj4nh4nIHumFe0txQf3z9MJWV","secretConfig":{},"secretVersion":10,"shouldApplyDeviceModeTransformation":false,"propagateEventsUntransformedOnError":false},{"id":"2LDaS7hJDi31TFg8ct5tIB9Lmfo","name":"Amp Dev","enabled":true,"config":{"apiKey":"1b0a913109d21ccaa5e315d9ef925dc2","groupTypeTrait":"","groupValueTrait":"","trackAllPages":false,"trackCategorizedPages":true,"trackNamedPages":true,"traitsToIncrement":[{"traits":""}],"traitsToSetOnce":[{"traits":""}],"traitsToAppend":[{"traits":""}],"traitsToPrepend":[{"traits":""}],"trackProductsOnce":false,"trackRevenuePerProduct":false,"versionName":"","trackSessionEvents":false,"useIdfaAsDeviceId":false,"blacklistedEvents":[{"eventName":""}],"whitelistedEvents":[{"eventName":""}],"oneTrustCookieCategories":[{"oneTrustCookieCategory":""}],"eventFilteringOption":"disable","mapDeviceBrand":false,"residencyServer":"standard","userProvidedPageEventString":"","useUserDefinedPageEventName":false,"eventUploadThreshold":"30","eventUploadPeriodMillis":"30000"},"liveEventsConfig":{"eventDelivery":false,"eventDeliveryTS":1681887360138},"destinationDefinitionId":"1Q8kZdhlglB9q8gsD4xryMyWg1i","destinationDefinition":{"id":"1Q8kZdhlglB9q8gsD4xryMyWg1i","name":"AM","displayName":"Amplitude","createdAt":"2019-08-30T10:07:47.170Z","updatedAt":"2023-11-23T06:21:22.977Z","config":{"destConfig":{"ios":["eventUploadPeriodMillis","eventUploadThreshold","useNativeSDK","connectionMode","trackSessionEvents","useIdfaAsDeviceId"],"web":["useNativeSDK","connectionMode","preferAnonymousIdForDeviceId","attribution","eventUploadThreshold","eventUploadPeriodMillis","trackNewCampaigns"],"android":["eventUploadPeriodMillis","eventUploadThreshold","useNativeSDK","connectionMode","enableLocationListening","trackSessionEvents","useAdvertisingIdForDeviceId"],"flutter":["eventUploadPeriodMillis","eventUploadThreshold","useNativeSDK","connectionMode","enableLocationListening","trackSessionEvents","useAdvertisingIdForDeviceId","useIdfaAsDeviceId"],"reactnative":["eventUploadPeriodMillis","eventUploadThreshold","useNativeSDK","connectionMode","enableLocationListening","trackSessionEvents","useAdvertisingIdForDeviceId","useIdfaAsDeviceId"],"defaultConfig":["apiKey","groupTypeTrait","groupValueTrait","trackAllPages","trackCategorizedPages","trackNamedPages","traitsToIncrement","traitsToSetOnce","traitsToAppend","traitsToPrepend","trackProductsOnce","trackRevenuePerProduct","versionName","apiSecret","residencyServer","blacklistedEvents","whitelistedEvents","eventFilteringOption","mapDeviceBrand","oneTrustCookieCategories","userProvidedPageEventString","useUserDefinedPageEventName","userProvidedScreenEventString","useUserDefinedScreenEventName"]},"secretKeys":["apiKey","apiSecret"],"excludeKeys":[],"includeKeys":["apiKey","groupTypeTrait","groupValueTrait","trackAllPages","trackCategorizedPages","trackNamedPages","traitsToIncrement","traitsToSetOnce","traitsToAppend","traitsToPrepend","trackProductsOnce","trackRevenuePerProduct","preferAnonymousIdForDeviceId","versionName","enableLocationListening","useAdvertisingIdForDeviceId","trackSessionEvents","useIdfaAsDeviceId","blacklistedEvents","whitelistedEvents","oneTrustCookieCategories","eventFilteringOption","mapDeviceBrand","residencyServer","userProvidedPageEventString","useUserDefinedPageEventName","attribution","eventUploadThreshold","eventUploadPeriodMillis","trackNewCampaigns","userProvidedScreenEventString","useUserDefinedScreenEventName"],"transformAt":"processor","transformAtV1":"processor","isAudienceSupported":false,"supportedSourceTypes":["android","ios","web","unity","amp","cloud","warehouse","reactnative","flutter","cordova","shopify"],"supportsVisualMapper":true,"supportedMessageTypes":["alias","group","identify","page","screen","track"],"saveDestinationResponse":true,"supportedConnectionModes":{"ios":["cloud","device"],"web":["cloud","device"],"android":["cloud","device"],"flutter":["cloud","device"],"reactnative":["cloud","device"]}},"responseRules":null,"category":null,"options":null},"deleted":false,"createdAt":"2023-02-03T07:08:23.373Z","updatedAt":"2023-10-03T12:10:02.379Z","workspaceId":"2CYviwoPKBATiXGmWYNpK5p2lPL","revisionId":"2WFj0vIHszakUtSWUc154AhTHAU","secretConfig":{},"secretVersion":11,"shouldApplyDeviceModeTransformation":false,"propagateEventsUntransformedOnError":false}],"updatedAt":"2023-11-20T06:53:35.875Z","createdAt":"2023-05-17T06:21:22.377Z","liveEventsConfig":{"eventUpload":false,"eventUploadTS":1700463215874},"sourceDefinition":{"options":null,"config":null,"configSchema":null,"uiConfig":null,"id":"1Q8kZmXcQ89PWXmdBlN3CSgkaO0","name":"iOS","displayName":"iOS","category":null,"type":"ios","createdAt":"2019-08-30T10:07:48.397Z","updatedAt":"2023-04-26T11:00:01.643Z"},"createdBy":"2CYvj0Lk1AEiT6gm920O3NemhUZ","secretVersion":null,"connections":[{"id":"2TQyKWhIJEu2BD6J8O1hYMlpi5o","sourceId":"2PuQRyVWk5fmstevuIYS9tTlQos","destinationId":"2Ku8x3PujDxvraFCvXeidOwNl4K","enabled":true,"config":null,"deleted":false,"createdAt":"2023-08-02T15:18:55.314Z","updatedAt":"2023-08-02T15:18:55.314Z"},{"id":"2W1pX0qcKJsuKYg5MrVvAP5QSY4","sourceId":"2PuQRyVWk5fmstevuIYS9tTlQos","destinationId":"2LDaS7hJDi31TFg8ct5tIB9Lmfo","enabled":true,"config":null,"deleted":false,"createdAt":"2023-09-28T14:06:15.880Z","updatedAt":"2023-09-28T14:06:15.880Z"}],"deleted":false,"transient":false},"updatedAt":"2023-11-24T09:28:55.672Z","consentManagementMetadata":{"providers":[{"provider":"oneTrust","resolutionStrategy":"and"},{"provider":"ketch","resolutionStrategy":"or"}]}} \ No newline at end of file diff --git a/Sources/Classes/Utility/MockURLProtocol.swift b/Sources/Classes/Utility/MockURLProtocol.swift new file mode 100644 index 00000000..c4bd625a --- /dev/null +++ b/Sources/Classes/Utility/MockURLProtocol.swift @@ -0,0 +1,40 @@ +// +// MockURLProtocol.swift +// MetricsReporterTests +// +// Created by Pallab Maiti on 27/06/23. +// + +class MockURLProtocol: URLProtocol { + + static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data?))? + + override class func canInit(with request: URLRequest) -> Bool { + return true + } + + override class func canonicalRequest(for request: URLRequest) -> URLRequest { + return request + } + + override func startLoading() { + guard let handler = MockURLProtocol.requestHandler else { + fatalError("Handler is unavailable.") + } + + do { + let (response, data) = try handler(request) + client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + if let data = data { + client?.urlProtocol(self, didLoad: data) + } + client?.urlProtocolDidFinishLoading(self) + } catch { + client?.urlProtocol(self, didFailWithError: error) + } + } + + override func stopLoading() { + + } +} diff --git a/Sources/Classes/Utility/RSUtils.swift b/Sources/Classes/Utility/RSUtils.swift index 59526737..bff63084 100644 --- a/Sources/Classes/Utility/RSUtils.swift +++ b/Sources/Classes/Utility/RSUtils.swift @@ -9,7 +9,25 @@ import Foundation import SQLite3 +internal var isUnitTesting: Bool = { + // this will work on apple platforms, but fail on linux. + if NSClassFromString("XCTestCase") != nil { + return true + } + // this will work on linux and apple platforms, but not in anything with a UI + // because XCTest doesn't come into the call stack till much later. + let matches = Thread.callStackSymbols.filter { line in + return line.contains("XCTest") || line.contains("xctest") + } + if !matches.isEmpty { + return true + } + // couldn't see anything that indicated we were testing. + return false +}() + struct RSUtils { + static func getDateString(date: Date) -> String { let dateFormatter = DateFormatter() dateFormatter.timeZone = TimeZone(abbreviation: "UTC") diff --git a/Sources/Classes/Utility/TestUtils.swift b/Sources/Classes/Utility/TestUtils.swift new file mode 100644 index 00000000..d5f222d7 --- /dev/null +++ b/Sources/Classes/Utility/TestUtils.swift @@ -0,0 +1,61 @@ +// +// TestUtils.swift +// RudderTests +// +// Created by Pallab Maiti on 10/02/23. +// + +import Foundation + +internal final class TestUtils { + + static let shared = TestUtils() + + func getPath(forResource: String, ofType: String) -> String { + let bundle = Bundle(for: type(of: self)) + if let path = bundle.path(forResource: forResource, ofType: ofType) { + return path + } else { + fatalError("\(forResource).\(ofType) not present in test bundle.") + } + } + + func getJSONString(forResource: String, ofType: String) -> String { + let path = getPath(forResource: forResource, ofType: ofType) + do { + let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) + let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) + let data1 = try JSONSerialization.data(withJSONObject: jsonResult, options: .prettyPrinted) + if let convertedString = String(data: data1, encoding: .utf8) { + return convertedString + } else { + fatalError("Can not parse or invalid JSON.") + } + } catch { + fatalError("Can not parse or invalid JSON.") + } + } + + func convertToDictionary(text: String) -> [String: String]? { + if let data = text.data(using: .utf8) { + do { + return try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] + } catch { + print(error.localizedDescription) + } + } + return nil + } + + func convertToJSONString(arrayObject: NSMutableArray) -> String? { + do { + let jsonData: Data = try JSONSerialization.data(withJSONObject: arrayObject, options: []) + if let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue) { + return jsonString as String + } + } catch { + print(error.localizedDescription) + } + return nil + } +} diff --git a/Sources/Rudder.h b/Sources/Rudder.h new file mode 100644 index 00000000..e33391b1 --- /dev/null +++ b/Sources/Rudder.h @@ -0,0 +1,18 @@ +// +// Rudder.h +// Rudder +// +// Created by Pallab Maiti on 09/03/22. +// + +#import + +//! Project version number for Rudder. +FOUNDATION_EXPORT double RSVersionNumber; + +//! Project version string for Rudder. +FOUNDATION_EXPORT const unsigned char RSVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Sources/SDKInfo/Info.plist b/Sources/SDKInfo/Info.plist deleted file mode 100644 index c0701c6d..00000000 --- a/Sources/SDKInfo/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - - diff --git a/Sources/SDKInfo/Rudder.h b/Sources/SDKInfo/Rudder.h deleted file mode 100644 index 54b38bed..00000000 --- a/Sources/SDKInfo/Rudder.h +++ /dev/null @@ -1,67 +0,0 @@ -// -// RS.h -// -// Created by Arnab Pal on 17/10/19. -// Copyright © 2019 RSlabs. All rights reserved. -// - -/*#import "RSClient.h" - -#import "RSConfig.h" -#import "RSConfigBuilder.h" - -#import "RSMessage.h" -#import "RSMessageBuilder.h" - -#import "RSLogger.h" - -// Ecommerce Section -#import "RSECommerceProductBuilder.h" -#import "RSECommerceFilterBuilder.h" -#import "RSECommerceSortBuilder.h" -#import "RSECommercePromotionBuilder.h" -#import "RSECommerceOrderBuilder.h" -#import "RSECommerceCheckoutBuilder.h" -#import "RSECommerceCouponBuilder.h" -#import "RSECommerceWishListBuilder.h" - -#import "RSProductSearchedEvent.h" -#import "RSProductListViewedEvent.h" -#import "RSProductListFilteredEvent.h" -#import "RSPromotionViewedEvent.h" -#import "RSPromotionClickedEvent.h" -#import "RSProductClickedEvent.h" -#import "RSProductViewedEvent.h" -#import "RSProductAddedToCartEvent.h" -#import "RSProductRemovedEvent.h" -#import "RSCartViewedEvent.h" -#import "RSCheckoutStartedEvent.h" -#import "RSCheckoutStepViewedEvent.h" -#import "RSCheckoutStepCompletedEvent.h" -#import "RSPaymentInfoEnteredEvent.h" -#import "RSOrderUpdatedEvent.h" -#import "RSOrderCompletedEvent.h" -#import "RSOrderRefundedEvent.h" -#import "RSOrderCancelledEvent.h" -#import "RSCouponEnteredEvent.h" -#import "RSCouponAppliedEvent.h" -#import "RSCouponDeniedEvent.h" -#import "RSCouponRemovedEvent.h" -#import "RSProductAddedToWishListEvent.h" -#import "RSProductRemovedFromWishListEvent.h" -#import "RSWishListProductAddedToCartEvent.h" -#import "RSProductSharedEvent.h" -#import "RSCartSharedEvent.h" -#import "RSProductReviewedEvent.h"*/ - -#import - -//! Project version number for Rudder. -FOUNDATION_EXPORT double RSVersionNumber; - -//! Project version string for Rudder. -FOUNDATION_EXPORT const unsigned char RSVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/Tests/RudderTests/RSAliasTests.swift b/Tests/RudderTests/RSAliasTests.swift deleted file mode 100644 index 07d705fe..00000000 --- a/Tests/RudderTests/RSAliasTests.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// RSAliasTests.swift -// RudderStackTests -// -// Created by Pallab Maiti on 09/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import XCTest -@testable import Rudder - -class RSAliasTests: XCTestCase { - - var client: RSClient! - - override func setUpWithError() throws { - client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) - } - - override func tearDownWithError() throws { - client = nil - } - - func testAlias() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.alias("user_id") - - let aliasEvent1 = resultPlugin.lastMessage as? AliasMessage - - XCTAssertTrue(aliasEvent1?.userId == "user_id") - XCTAssertTrue(aliasEvent1?.type == .alias) - XCTAssertNil(aliasEvent1?.option) - XCTAssertNil(aliasEvent1?.previousId) - - client.alias("new_user_id") - - let aliasEvent2 = resultPlugin.lastMessage as? AliasMessage - - XCTAssertTrue(aliasEvent2?.userId == "new_user_id") - XCTAssertTrue(aliasEvent2?.previousId == "user_id") - XCTAssertTrue(aliasEvent2?.type == .alias) - XCTAssertNil(aliasEvent2?.option) - } -} diff --git a/Tests/RudderTests/RSClientTests.swift b/Tests/RudderTests/RSClientTests.swift deleted file mode 100644 index 93ddc1e8..00000000 --- a/Tests/RudderTests/RSClientTests.swift +++ /dev/null @@ -1,235 +0,0 @@ -// -// RSClientTests.swift -// RudderStackTests -// -// Created by Pallab Maiti on 07/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import XCTest -@testable import Rudder - -let WRITE_KEY = "1wvsoF3Kx2SczQNlx1dvcqW9ODW" -let DATA_PLANE_URL = "https://rudderstacz.dataplane.rudderstack.com" - -class RSClientTests: XCTestCase { - - var client: RSClient! - - override func setUpWithError() throws { - client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) - } - - override func tearDownWithError() throws { - client = nil - } - - func testBaseEventCreation() { - client.track("Track 1") - } - - // make sure you have Firebase added & enabled to the source in your RudderStack A/C - func testDestinationEnabled() { - let expectation = XCTestExpectation(description: "Firebase Expectation") - let myDestination = FirebaseDestination { - expectation.fulfill() - return true - } - - client.addDestination(myDestination) - waitUntilServerConfigDownloaded(client: client) - waitUntilStarted(client: client) - client.track("testDestinationEnabled") - - wait(for: [expectation], timeout: 2.0) - } - - func testDestinationNotEnabled() { - let expectation = XCTestExpectation(description: "MyDestination Expectation") - let myDestination = MyDestination { - expectation.fulfill() - return true - } - - client.addDestination(myDestination) - waitUntilServerConfigDownloaded(client: client) - waitUntilStarted(client: client) - client.track("testDestinationEnabled") - - XCTExpectFailure { - wait(for: [expectation], timeout: 2.0) - } - } - - func testAnonymousId() { - client.setAnonymousId("anonymous_id") - - let anonId = client.anonymousId - - XCTAssertTrue(anonId != "") - XCTAssertTrue(anonId == "anonymous_id") - } - - func testContext() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.track("context check") - - let context = resultPlugin.lastMessage?.context - XCTAssertNotNil(context) - XCTAssertNotNil(context?["screen"], "screen missing!") - XCTAssertNotNil(context?["network"], "network missing!") - XCTAssertNotNil(context?["os"], "os missing!") - XCTAssertNotNil(context?["timezone"], "timezone missing!") - XCTAssertNotNil(context?["library"], "library missing!") - XCTAssertNotNil(context?["device"], "device missing!") - XCTAssertNotNil(context?["app"], "app missing!") - XCTAssertNotNil(context?["locale"], "locale missing!") - } - - func testDeviceToken() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.setDeviceToken("device_token") - client.track("device token check") - - let context = resultPlugin.lastMessage?.context - let token = context?[keyPath: "device.token"] as? String - - XCTAssertTrue(token != "") - XCTAssertTrue(token == "device_token") - } - - func testContextTraits() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.identify("user_id", traits: ["email": "abc@def.com"]) - - let identifyEvent = resultPlugin.lastMessage as? IdentifyMessage - XCTAssertTrue(identifyEvent?.userId == "user_id") - let identifyTraits = identifyEvent?.traits - XCTAssertTrue(identifyTraits?["email"] as? String == "abc@def.com") - - client.track("test context") - - let trackEvent = resultPlugin.lastMessage as? TrackMessage - XCTAssertTrue(trackEvent?.userId == "user_id") - let trackTraits = trackEvent?.context?["traits"] as? [String: Any] - XCTAssertNotNil(trackTraits) - XCTAssertTrue(trackTraits?["email"] as? String == "abc@def.com") - XCTAssertTrue(trackTraits?["userId"] as? String == "user_id") - - let clientTraits = client.traits - XCTAssertNotNil(clientTraits) - XCTAssertTrue(clientTraits?["email"] == "abc@def.com") - XCTAssertTrue(clientTraits?["userId"] == "user_id") - } -} - -func waitUntilStarted(client: RSClient?) { - guard let client = client else { return } - if let replayQueue = client.find(pluginType: RSReplayQueuePlugin.self) { - while replayQueue.running == true { - RunLoop.main.run(until: Date.distantPast) - } - } -} - -func waitUntilServerConfigDownloaded(client: RSClient?) { - guard let client = client else { return } - while client.serverConfig == nil { - RunLoop.main.run(until: Date.distantPast) - } -} - -class FirebaseDestinationPlugin: RSDestinationPlugin { - var controller: RSController = RSController() - var client: RSClient? - var type: PluginType = .destination - var key: String = "Firebase" - - let trackCompletion: (() -> Bool)? - - init(trackCompletion: (() -> Bool)? = nil) { - self.trackCompletion = trackCompletion - } - - func track(message: TrackMessage) -> TrackMessage? { - var returnEvent: TrackMessage? = message - if let completion = trackCompletion { - if !completion() { - returnEvent = nil - } - } - return returnEvent - } -} - -class MyDestinationPlugin: RSDestinationPlugin { - var controller: RSController = RSController() - var client: RSClient? - var type: PluginType = .destination - var key: String = "MyDestination" - - let trackCompletion: (() -> Bool)? - - init(trackCompletion: (() -> Bool)? = nil) { - self.trackCompletion = trackCompletion - } - - func track(message: TrackMessage) -> TrackMessage? { - var returnEvent: TrackMessage? = message - if let completion = trackCompletion { - if !completion() { - returnEvent = nil - } - } - return returnEvent - } -} - -class FirebaseDestination: RudderDestination { - init(trackCompletion: (() -> Bool)?) { - super.init() - plugin = FirebaseDestinationPlugin(trackCompletion: trackCompletion) - } -} - -class MyDestination: RudderDestination { - init(trackCompletion: (() -> Bool)?) { - super.init() - plugin = MyDestinationPlugin(trackCompletion: trackCompletion) - } -} - -class ResultPlugin: RSPlugin { - let type: PluginType = .after - var client: RSClient? - var lastMessage: RSMessage? - var trackList = [TrackMessage]() - var identifyList = [IdentifyMessage]() - - func execute(message: T?) -> T? where T: RSMessage { - lastMessage = message - if let message = message as? TrackMessage { - trackList.append(message) - } - if let message = message as? IdentifyMessage { - identifyList.append(message) - } - return message - } -} diff --git a/Tests/RudderTests/RSGroupTests.swift b/Tests/RudderTests/RSGroupTests.swift deleted file mode 100644 index 3cf09dc3..00000000 --- a/Tests/RudderTests/RSGroupTests.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// RSGroupTests.swift -// RudderStackTests -// -// Created by Pallab Maiti on 09/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import XCTest -@testable import Rudder - -class RSGroupTests: XCTestCase { - - var client: RSClient! - - override func setUpWithError() throws { - client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) - } - - override func tearDownWithError() throws { - client = nil - } - - func testGroup() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.group("sample_group_id") - - let groupEvent = resultPlugin.lastMessage as? GroupMessage - - XCTAssertTrue(groupEvent?.groupId == "sample_group_id") - XCTAssertTrue(groupEvent?.type == .group) - XCTAssertNil(groupEvent?.traits) - XCTAssertNil(groupEvent?.option) - } - - func testGroupWithTraits() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.group("sample_group_id", traits: ["key_1": "value_1", "key_2": "value_2"]) - - let groupEvent = resultPlugin.lastMessage as? GroupMessage - - XCTAssertTrue(groupEvent?.groupId == "sample_group_id") - XCTAssertTrue(groupEvent?.type == .group) - XCTAssertNotNil(groupEvent?.traits) - XCTAssertNil(groupEvent?.option) - - let traits = groupEvent?.traits - - XCTAssertTrue(traits?["key_1"] == "value_1") - XCTAssertTrue(traits?["key_2"] == "value_2") - } -} diff --git a/Tests/RudderTests/RSIdentifyTests.swift b/Tests/RudderTests/RSIdentifyTests.swift deleted file mode 100644 index 15bec4e8..00000000 --- a/Tests/RudderTests/RSIdentifyTests.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// RSIdentifyTests.swift -// RudderStackTests -// -// Created by Pallab Maiti on 09/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import XCTest -@testable import Rudder - -class RSIdentifyTests: XCTestCase { - - var client: RSClient! - - override func setUpWithError() throws { - client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) - } - - override func tearDownWithError() throws { - client = nil - } - - func testIdentify() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.identify("user_id") - - let identifyEvent = resultPlugin.lastMessage as? IdentifyMessage - - XCTAssertTrue(identifyEvent?.userId == "user_id") - XCTAssertTrue(identifyEvent?.type == .identify) - } - - func testIdentifyWithTraits() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.identify("user_id", traits: ["email": "abc@def.com"]) - - let identifyEvent = resultPlugin.lastMessage as? IdentifyMessage - - XCTAssertTrue(identifyEvent?.userId == "user_id") - XCTAssertTrue(identifyEvent?.type == .identify) - - let traits = identifyEvent?.traits - - XCTAssertTrue(traits?["email"] as? String == "abc@def.com") - XCTAssertFalse(traits?["name"] as? String == "name") - } - - func testUserIdAndTraitsPersistCorrectly() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.identify("user_id", traits: ["email": "abc@def.com"]) - - let identifyEvent = resultPlugin.lastMessage as? IdentifyMessage - - XCTAssertTrue(identifyEvent?.userId == "user_id") - XCTAssertTrue(identifyEvent?.type == .identify) - - let traits = identifyEvent?.traits - - XCTAssertTrue(traits?["email"] as? String == "abc@def.com") - XCTAssertFalse(traits?["name"] as? String == "name") - - client.track("simple_track") - - let trackEvent = resultPlugin.lastMessage as? TrackMessage - - XCTAssertTrue(trackEvent?.userId == "user_id") - let trackTraits = trackEvent?.context?["traits"] as? [String: Any] - XCTAssertNil(trackTraits) - } -} diff --git a/Tests/RudderTests/RSScreenTests.swift b/Tests/RudderTests/RSScreenTests.swift deleted file mode 100644 index 880c6818..00000000 --- a/Tests/RudderTests/RSScreenTests.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// RSScreenTests.swift -// RudderStackTests -// -// Created by Pallab Maiti on 09/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import XCTest -@testable import Rudder - -class RSScreenTests: XCTestCase { - - var client: RSClient! - - override func setUpWithError() throws { - client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) - } - - override func tearDownWithError() throws { - client = nil - } - - func testScreen() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.screen("ViewController") - - let screenEvent = resultPlugin.lastMessage as? ScreenMessage - XCTAssertTrue(screenEvent?.name == "ViewController") - XCTAssertTrue(screenEvent?.type == .screen) - } - - func testScreenWithProperties() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.screen("ViewController", properties: ["key_1": "value_1", "key_2": "value_2"]) - - let screenEvent = resultPlugin.lastMessage as? ScreenMessage - - XCTAssertTrue(screenEvent?.name == "ViewController") - XCTAssertTrue(screenEvent?.type == .screen) - XCTAssertNotNil(screenEvent?.properties) - XCTAssertNil(screenEvent?.option) - - let properties = screenEvent?.properties - - XCTAssertTrue(properties?["key_1"] as? String == "value_1") - XCTAssertTrue(properties?["key_2"] as? String == "value_2") - } -} diff --git a/Tests/RudderTests/RSTrackTests.swift b/Tests/RudderTests/RSTrackTests.swift deleted file mode 100644 index 17cb9ec1..00000000 --- a/Tests/RudderTests/RSTrackTests.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// RSTrackTests.swift -// RudderStackTests -// -// Created by Pallab Maiti on 09/03/22. -// Copyright © 2022 Rudder Labs India Pvt Ltd. All rights reserved. -// - -import XCTest -@testable import Rudder - -class RSTrackTests: XCTestCase { - var client: RSClient! - - override func setUpWithError() throws { - client = RSClient.sharedInstance() - client.configure(with: RSConfig(writeKey: WRITE_KEY).dataPlaneURL(DATA_PLANE_URL)) - } - - override func tearDownWithError() throws { - client = nil - } - - func testTrack() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.track("simple_track") - - let trackEvent = resultPlugin.lastMessage as? TrackMessage - - XCTAssertTrue(trackEvent?.event == "simple_track") - XCTAssertTrue(trackEvent?.type == .track) - XCTAssertNil(trackEvent?.properties) - XCTAssertNil(trackEvent?.option) - } - - func testTrackWithProperties() { - let resultPlugin = ResultPlugin() - client.add(plugin: resultPlugin) - - waitUntilStarted(client: client) - waitUntilServerConfigDownloaded(client: client) - - client.track("simple_track_with_props", properties: ["key_1": "value_1", "key_2": "value_2"]) - - let trackEvent = resultPlugin.lastMessage as? TrackMessage - - XCTAssertTrue(trackEvent?.event == "simple_track_with_props") - XCTAssertTrue(trackEvent?.type == .track) - XCTAssertNotNil(trackEvent?.properties) - XCTAssertNil(trackEvent?.option) - - let properties = trackEvent?.properties - - XCTAssertTrue(properties?["key_1"] as? String == "value_1") - XCTAssertTrue(properties?["key_2"] as? String == "value_2") - } - -} diff --git a/sonar-project.properties b/sonar-project.properties index 7c12c935..9355cecd 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -13,14 +13,14 @@ sonar.links.scm=https://github.com/rudderlabs/rudder-sdk-ios sonar.links.issue=https://github.com/rudderlabs/rudder-sdk-ios/issues # Path to reports -# sonar.javascript.lcov.reportPaths=reports/Test_report.json -# sonar.testExecutionReportPaths=reports/sonar/results-report.xml -# sonar.swift.swiftLint.reportPaths=reports/Lint_report.json +sonar.coverageReportPaths=generic-coverage.xml +sonar.c.file.suffixes=- +sonar.cpp.file.suffixes=- +sonar.objc.file.suffixes=- # Path to sources -sonar.sources=Sources/Classes -# sonar.inclusions=**/*.swift -# sonar.exclusions=**/*.json,**/*.html,**/*.png,**/*.jpg,**/*.gif,**/*.svg +sonar.sources=Sources +sonar.exclusions=**/*.json,**/*.html,**/*.png,**/*.jpg,**/*.gif,**/*.svg # Path to tests # sonar.tests=Tests diff --git a/xccov-to-generic.sh b/xccov-to-generic.sh new file mode 100644 index 00000000..0574a996 --- /dev/null +++ b/xccov-to-generic.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# xccov-to-generic.sh +# +# Created by Pallab Maiti. +# + +set -euo pipefail + +function convert_file { + local xccovarchive_file="$1" + local file_name="$2" + local xccov_options="$3" + echo " " + xcrun xccov view $xccov_options --file "$file_name" "$xccovarchive_file" | \ + sed -n ' + s/^ *\([0-9][0-9]*\): 0.*$/ /p; + s/^ *\([0-9][0-9]*\): [1-9].*$/ /p + ' + echo ' ' +} + +function xccov_to_generic { + echo '' + for xccovarchive_file in "$@"; do + if [[ ! -d $xccovarchive_file ]] + then + echo "Coverage FILE NOT FOUND AT PATH: $xccovarchive_file" 1>&2; + exit 1 + fi + + if [ $sdk_version -gt 10 ]; then # Apply optimization + xccovarchive_file=$(optimize_format) + fi + + local xccov_options="" + if [[ $xccovarchive_file == *".xcresult"* ]]; then + xccov_options="--archive" + fi + xcrun xccov view $xccov_options --file-list "$xccovarchive_file" | while read -r file_name; do + convert_file "$xccovarchive_file" "$file_name" "$xccov_options" + done + done + echo '' +} + +function check_sdk_version { + sdk_major_version=`xcrun --show-sdk-version | cut -d . -f 1` + + if [ $? -ne 0 ]; then + echo 'Failed to execute xcrun show-sdk-version' 1>&2 + exit -1 + fi + echo $sdk_major_version +} + +function cleanup_tmp_files { + rm -rf tmp.json + rm -rf tmp.xccovarchive +} + +# Optimize coverage files conversion time by exporting to a clean xcodearchive directory +# Credits to silverhammermba on issue #68 for the suggestion +function optimize_format { + cleanup_tmp_files + xcrun xcresulttool get --format json --path "$xccovarchive_file" > tmp.json + if [ $? -ne 0 ]; then + echo 'Failed to execute xcrun xcresulttool get' 1>&2 + exit -1 + fi + + # local reference=$(jq -r '.actions._values[2].actionResult.coverage.archiveRef.id._value' tmp.json) + local reference=$(jq -r '.actions._values[]|[.actionResult.coverage.archiveRef.id],._values' tmp.json | grep value | cut -d : -f 2 | cut -d \" -f 2) + if [ $? -ne 0 ]; then + echo 'Failed to execute jq (https://stedolan.github.io/jq/)' 1>&2 + exit -1 + fi + # $reference can be a list of IDs (from a merged .xcresult bundle of multiple test plans) + for test_ref in $reference; do + xcrun xcresulttool export --type directory --path "$xccovarchive_file" --id "$test_ref" --output-path tmp.xccovarchive + if [ $? -ne 0 ]; then + echo "Failed to execute xcrun xcresulttool export for reference ${test_ref}" 1>&2 + exit -1 + fi + done + echo "tmp.xccovarchive" +} + +sdk_version=$(check_sdk_version) +if [ $? -ne 0 ]; then + exit -1 +fi + +xccov_to_generic "$@" +cleanup_tmp_files