From 2615b7aa0de0b46e27d9437108d5236a2541588e Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Fri, 4 Mar 2022 10:21:55 -0800 Subject: [PATCH] Moved supported destinations to individual repos --- .../project.pbxproj | 157 ++++---- .../DestinationsExample/AppDelegate.swift | 5 + .../AmplitudeSession.swift | 167 --------- .../AppsFlyerDestination.swift | 348 ------------------ .../FacebookAppEventsDestination.swift | 180 --------- .../FirebaseDestination.swift | 228 ------------ .../MixpanelDestination.swift | 342 ----------------- 7 files changed, 82 insertions(+), 1345 deletions(-) delete mode 100644 Examples/destination_plugins/AmplitudeSession.swift delete mode 100644 Examples/destination_plugins/AppsFlyerDestination.swift delete mode 100644 Examples/destination_plugins/FacebookAppEventsDestination.swift delete mode 100644 Examples/destination_plugins/FirebaseDestination.swift delete mode 100644 Examples/destination_plugins/MixpanelDestination.swift diff --git a/Examples/apps/DestinationsExample/DestinationsExample.xcodeproj/project.pbxproj b/Examples/apps/DestinationsExample/DestinationsExample.xcodeproj/project.pbxproj index 8c298f44..69aee496 100644 --- a/Examples/apps/DestinationsExample/DestinationsExample.xcodeproj/project.pbxproj +++ b/Examples/apps/DestinationsExample/DestinationsExample.xcodeproj/project.pbxproj @@ -12,12 +12,14 @@ 46871696270E16080028B595 /* ConsentTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46871690270E16080028B595 /* ConsentTracking.swift */; }; 46871697270E16080028B595 /* IDFACollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46871691270E16080028B595 /* IDFACollection.swift */; }; 46871698270E16080028B595 /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46871692270E16080028B595 /* ConsoleLogger.swift */; }; + 468DE06527D28C6500F2D94B /* SegmentAmplitude in Frameworks */ = {isa = PBXBuildFile; productRef = 468DE06427D28C6500F2D94B /* SegmentAmplitude */; }; + 468DE06827D28C7E00F2D94B /* SegmentAppsFlyer in Frameworks */ = {isa = PBXBuildFile; productRef = 468DE06727D28C7E00F2D94B /* SegmentAppsFlyer */; }; + 468DE06B27D28CB200F2D94B /* SegmentFacebook in Frameworks */ = {isa = PBXBuildFile; productRef = 468DE06A27D28CB200F2D94B /* SegmentFacebook */; }; + 468DE06E27D28D1300F2D94B /* SegmentFirebase in Frameworks */ = {isa = PBXBuildFile; productRef = 468DE06D27D28D1300F2D94B /* SegmentFirebase */; }; + 468DE07127D28D4700F2D94B /* SegmentMixpanel in Frameworks */ = {isa = PBXBuildFile; productRef = 468DE07027D28D4700F2D94B /* SegmentMixpanel */; }; 4694D2F627B58C9600D110C0 /* FlurryAnalyticsSPM in Frameworks */ = {isa = PBXBuildFile; productRef = 4694D2F527B58C9600D110C0 /* FlurryAnalyticsSPM */; }; 4694D2F927B58E5E00D110C0 /* ComScore in Frameworks */ = {isa = PBXBuildFile; productRef = 4694D2F827B58E5E00D110C0 /* ComScore */; }; - 4694D2FC27B5902100D110C0 /* Mixpanel in Frameworks */ = {isa = PBXBuildFile; productRef = 4694D2FB27B5902100D110C0 /* Mixpanel */; }; 4694D2FF27B5914200D110C0 /* Intercom in Frameworks */ = {isa = PBXBuildFile; productRef = 4694D2FE27B5914200D110C0 /* Intercom */; }; - 4694D30127B592E500D110C0 /* FacebookAppEventsDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4694D30027B592E500D110C0 /* FacebookAppEventsDestination.swift */; }; - 4694D30327B5979100D110C0 /* FacebookCore in Frameworks */ = {isa = PBXBuildFile; productRef = 4694D30227B5979100D110C0 /* FacebookCore */; }; 469EC8D0266066130068F9E3 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 469EC8CF266066130068F9E3 /* SystemConfiguration.framework */; }; 469F7B08266011690038E773 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469F7B07266011690038E773 /* AppDelegate.swift */; }; 469F7B0A266011690038E773 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469F7B09266011690038E773 /* SceneDelegate.swift */; }; @@ -30,16 +32,10 @@ 469F7B23266013100038E773 /* Adjust in Frameworks */ = {isa = PBXBuildFile; productRef = 469F7B22266013100038E773 /* Adjust */; }; 469F7B25266013320038E773 /* AdjustDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469F7B24266013320038E773 /* AdjustDestination.swift */; }; 96469A9B270279A600AC5772 /* IntercomDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96469A9A270279A600AC5772 /* IntercomDestination.swift */; }; - 965DC0FA2668077400DDF9C7 /* MixpanelDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */; }; - 965DC0FB2668077400DDF9C7 /* AmplitudeSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965DC0F92668077400DDF9C7 /* AmplitudeSession.swift */; }; - 965DC1212669942800DDF9C7 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 965DC1202669942800DDF9C7 /* FirebaseAnalytics */; }; - 965DC1232669947F00DDF9C7 /* FirebaseDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965DC1222669947F00DDF9C7 /* FirebaseDestination.swift */; }; 965DC1262671656C00DDF9C7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 965DC1252671656C00DDF9C7 /* GoogleService-Info.plist */; }; 9697C1F52679156C00B87EC1 /* Segment_Logo_Avatar_Grey-1024.png in Resources */ = {isa = PBXBuildFile; fileRef = 9697C1F42679156C00B87EC1 /* Segment_Logo_Avatar_Grey-1024.png */; }; 96D8F16F26EFFA09007F8B28 /* ExampleDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96D8F16E26EFFA09007F8B28 /* ExampleDestination.swift */; }; 96DBF37D26FA943300724B0B /* ComscoreDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96DBF37C26FA943300724B0B /* ComscoreDestination.swift */; }; - BA384C9826824F3700AFEA1B /* AppsFlyerLib in Frameworks */ = {isa = PBXBuildFile; productRef = BA384C9726824F3700AFEA1B /* AppsFlyerLib */; }; - BA384C9A2682973300AFEA1B /* AppsFlyerDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA384C992682973300AFEA1B /* AppsFlyerDestination.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -48,7 +44,6 @@ 46871690270E16080028B595 /* ConsentTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentTracking.swift; sourceTree = ""; }; 46871691270E16080028B595 /* IDFACollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IDFACollection.swift; sourceTree = ""; }; 46871692270E16080028B595 /* ConsoleLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = ""; }; - 4694D30027B592E500D110C0 /* FacebookAppEventsDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FacebookAppEventsDestination.swift; sourceTree = ""; }; 469EC8CF266066130068F9E3 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 469EC8E1266828AF0068F9E3 /* DestinationsExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DestinationsExample-Bridging-Header.h"; sourceTree = ""; }; 469F7B04266011690038E773 /* DestinationsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DestinationsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -62,14 +57,10 @@ 469F7B1F266012CB0038E773 /* FlurryDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlurryDestination.swift; sourceTree = ""; }; 469F7B24266013320038E773 /* AdjustDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdjustDestination.swift; sourceTree = ""; }; 96469A9A270279A600AC5772 /* IntercomDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntercomDestination.swift; sourceTree = ""; }; - 965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelDestination.swift; sourceTree = ""; }; - 965DC0F92668077400DDF9C7 /* AmplitudeSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmplitudeSession.swift; sourceTree = ""; }; - 965DC1222669947F00DDF9C7 /* FirebaseDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseDestination.swift; sourceTree = ""; }; 965DC1252671656C00DDF9C7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 9697C1F42679156C00B87EC1 /* Segment_Logo_Avatar_Grey-1024.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Segment_Logo_Avatar_Grey-1024.png"; sourceTree = ""; }; 96D8F16E26EFFA09007F8B28 /* ExampleDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleDestination.swift; sourceTree = ""; }; 96DBF37C26FA943300724B0B /* ComscoreDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComscoreDestination.swift; sourceTree = ""; }; - BA384C992682973300AFEA1B /* AppsFlyerDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppsFlyerDestination.swift; sourceTree = ""; }; BA384C9D2686609000AFEA1B /* DestinationsExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DestinationsExample.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ @@ -78,14 +69,15 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BA384C9826824F3700AFEA1B /* AppsFlyerLib in Frameworks */, - 4694D30327B5979100D110C0 /* FacebookCore in Frameworks */, + 468DE06B27D28CB200F2D94B /* SegmentFacebook in Frameworks */, + 468DE06E27D28D1300F2D94B /* SegmentFirebase in Frameworks */, 469EC8D0266066130068F9E3 /* SystemConfiguration.framework in Frameworks */, + 468DE06827D28C7E00F2D94B /* SegmentAppsFlyer in Frameworks */, 4694D2F627B58C9600D110C0 /* FlurryAnalyticsSPM in Frameworks */, - 965DC1212669942800DDF9C7 /* FirebaseAnalytics in Frameworks */, + 468DE07127D28D4700F2D94B /* SegmentMixpanel in Frameworks */, 4694D2F927B58E5E00D110C0 /* ComScore in Frameworks */, 469F7B1D266011D70038E773 /* Segment in Frameworks */, - 4694D2FC27B5902100D110C0 /* Mixpanel in Frameworks */, + 468DE06527D28C6500F2D94B /* SegmentAmplitude in Frameworks */, 4694D2FF27B5914200D110C0 /* Intercom in Frameworks */, 469F7B23266013100038E773 /* Adjust in Frameworks */, ); @@ -154,16 +146,11 @@ 469F7B1E266012CB0038E773 /* destination_plugins */ = { isa = PBXGroup; children = ( - BA384C992682973300AFEA1B /* AppsFlyerDestination.swift */, 469F7B24266013320038E773 /* AdjustDestination.swift */, - 965DC0F92668077400DDF9C7 /* AmplitudeSession.swift */, 96DBF37C26FA943300724B0B /* ComscoreDestination.swift */, - 4694D30027B592E500D110C0 /* FacebookAppEventsDestination.swift */, 96D8F16E26EFFA09007F8B28 /* ExampleDestination.swift */, - 965DC1222669947F00DDF9C7 /* FirebaseDestination.swift */, 469F7B1F266012CB0038E773 /* FlurryDestination.swift */, 96469A9A270279A600AC5772 /* IntercomDestination.swift */, - 965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */, ); name = destination_plugins; path = ../../../destination_plugins; @@ -197,13 +184,14 @@ packageProductDependencies = ( 469F7B1C266011D70038E773 /* Segment */, 469F7B22266013100038E773 /* Adjust */, - 965DC1202669942800DDF9C7 /* FirebaseAnalytics */, - BA384C9726824F3700AFEA1B /* AppsFlyerLib */, 4694D2F527B58C9600D110C0 /* FlurryAnalyticsSPM */, 4694D2F827B58E5E00D110C0 /* ComScore */, - 4694D2FB27B5902100D110C0 /* Mixpanel */, 4694D2FE27B5914200D110C0 /* Intercom */, - 4694D30227B5979100D110C0 /* FacebookCore */, + 468DE06427D28C6500F2D94B /* SegmentAmplitude */, + 468DE06727D28C7E00F2D94B /* SegmentAppsFlyer */, + 468DE06A27D28CB200F2D94B /* SegmentFacebook */, + 468DE06D27D28D1300F2D94B /* SegmentFirebase */, + 468DE07027D28D4700F2D94B /* SegmentMixpanel */, ); productName = DestinationsExample; productReference = 469F7B04266011690038E773 /* DestinationsExample.app */; @@ -234,13 +222,14 @@ mainGroup = 469F7AFB266011690038E773; packageReferences = ( 469F7B21266013100038E773 /* XCRemoteSwiftPackageReference "ios_sdk" */, - 965DC11F2669942800DDF9C7 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, - BA384C9626824F3700AFEA1B /* XCRemoteSwiftPackageReference "AppsFlyerFramework" */, - 4694D2F327B4500700D110C0 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */, 4694D2F427B58C9500D110C0 /* XCRemoteSwiftPackageReference "FlurrySwiftPackage" */, 4694D2F727B58E5E00D110C0 /* XCRemoteSwiftPackageReference "comscore-swift-package-manager" */, - 4694D2FA27B5902100D110C0 /* XCRemoteSwiftPackageReference "mixpanel-swift" */, 4694D2FD27B5914200D110C0 /* XCRemoteSwiftPackageReference "intercom-ios" */, + 468DE06327D28C6500F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-amplitude" */, + 468DE06627D28C7E00F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-appsflyer" */, + 468DE06927D28CB200F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-facebook-app-events" */, + 468DE06C27D28D1300F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-firebase" */, + 468DE06F27D28D4700F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-mixpanel" */, ); productRefGroup = 469F7B05266011690038E773 /* Products */; projectDirPath = ""; @@ -271,21 +260,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - BA384C9A2682973300AFEA1B /* AppsFlyerDestination.swift in Sources */, 46871698270E16080028B595 /* ConsoleLogger.swift in Sources */, 469F7B20266012CB0038E773 /* FlurryDestination.swift in Sources */, 469F7B0C266011690038E773 /* ViewController.swift in Sources */, 96469A9B270279A600AC5772 /* IntercomDestination.swift in Sources */, 96D8F16F26EFFA09007F8B28 /* ExampleDestination.swift in Sources */, - 965DC0FA2668077400DDF9C7 /* MixpanelDestination.swift in Sources */, 96DBF37D26FA943300724B0B /* ComscoreDestination.swift in Sources */, - 965DC0FB2668077400DDF9C7 /* AmplitudeSession.swift in Sources */, 469F7B08266011690038E773 /* AppDelegate.swift in Sources */, 46871694270E16080028B595 /* NotificationTracking.swift in Sources */, 469F7B25266013320038E773 /* AdjustDestination.swift in Sources */, 469F7B0A266011690038E773 /* SceneDelegate.swift in Sources */, - 4694D30127B592E500D110C0 /* FacebookAppEventsDestination.swift in Sources */, - 965DC1232669947F00DDF9C7 /* FirebaseDestination.swift in Sources */, 46871695270E16080028B595 /* UIKitScreenTracking.swift in Sources */, 46871696270E16080028B595 /* ConsentTracking.swift in Sources */, 46871697270E16080028B595 /* IDFACollection.swift in Sources */, @@ -500,73 +484,106 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 4694D2F327B4500700D110C0 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */ = { + 468DE06327D28C6500F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-amplitude" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/facebook/facebook-ios-sdk"; + repositoryURL = "https://github.com/segment-integrations/analytics-swift-amplitude"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 12.0.0; + minimumVersion = 1.1.3; }; }; - 4694D2F427B58C9500D110C0 /* XCRemoteSwiftPackageReference "FlurrySwiftPackage" */ = { + 468DE06627D28C7E00F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-appsflyer" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/flurry/FlurrySwiftPackage"; + repositoryURL = "https://github.com/segment-integrations/analytics-swift-appsflyer"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 11.0.0; + minimumVersion = 1.1.3; }; }; - 4694D2F727B58E5E00D110C0 /* XCRemoteSwiftPackageReference "comscore-swift-package-manager" */ = { + 468DE06927D28CB200F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-facebook-app-events" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/comscore/comscore-swift-package-manager"; + repositoryURL = "https://github.com/segment-integrations/analytics-swift-facebook-app-events"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 6.9.0; + minimumVersion = 1.1.3; }; }; - 4694D2FA27B5902100D110C0 /* XCRemoteSwiftPackageReference "mixpanel-swift" */ = { + 468DE06C27D28D1300F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-firebase" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/mixpanel/mixpanel-swift"; + repositoryURL = "https://github.com/segment-integrations/analytics-swift-firebase"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 3.1.3; + minimumVersion = 1.1.3; }; }; - 4694D2FD27B5914200D110C0 /* XCRemoteSwiftPackageReference "intercom-ios" */ = { + 468DE06F27D28D4700F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-mixpanel" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/intercom/intercom-ios"; + repositoryURL = "https://github.com/segment-integrations/analytics-swift-mixpanel"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 11.1.2; + minimumVersion = 1.1.3; }; }; - 469F7B21266013100038E773 /* XCRemoteSwiftPackageReference "ios_sdk" */ = { + 4694D2F427B58C9500D110C0 /* XCRemoteSwiftPackageReference "FlurrySwiftPackage" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/adjust/ios_sdk.git"; + repositoryURL = "https://github.com/flurry/FlurrySwiftPackage"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 4.29.6; + minimumVersion = 11.0.0; }; }; - 965DC11F2669942800DDF9C7 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + 4694D2F727B58E5E00D110C0 /* XCRemoteSwiftPackageReference "comscore-swift-package-manager" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; + repositoryURL = "https://github.com/comscore/comscore-swift-package-manager"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 8.1.0; + minimumVersion = 6.9.0; }; }; - BA384C9626824F3700AFEA1B /* XCRemoteSwiftPackageReference "AppsFlyerFramework" */ = { + 4694D2FD27B5914200D110C0 /* XCRemoteSwiftPackageReference "intercom-ios" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/AppsFlyerSDK/AppsFlyerFramework"; + repositoryURL = "https://github.com/intercom/intercom-ios"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 6.3.2; + minimumVersion = 11.1.2; + }; + }; + 469F7B21266013100038E773 /* XCRemoteSwiftPackageReference "ios_sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/adjust/ios_sdk.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.29.6; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 468DE06427D28C6500F2D94B /* SegmentAmplitude */ = { + isa = XCSwiftPackageProductDependency; + package = 468DE06327D28C6500F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-amplitude" */; + productName = SegmentAmplitude; + }; + 468DE06727D28C7E00F2D94B /* SegmentAppsFlyer */ = { + isa = XCSwiftPackageProductDependency; + package = 468DE06627D28C7E00F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-appsflyer" */; + productName = SegmentAppsFlyer; + }; + 468DE06A27D28CB200F2D94B /* SegmentFacebook */ = { + isa = XCSwiftPackageProductDependency; + package = 468DE06927D28CB200F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-facebook-app-events" */; + productName = SegmentFacebook; + }; + 468DE06D27D28D1300F2D94B /* SegmentFirebase */ = { + isa = XCSwiftPackageProductDependency; + package = 468DE06C27D28D1300F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-firebase" */; + productName = SegmentFirebase; + }; + 468DE07027D28D4700F2D94B /* SegmentMixpanel */ = { + isa = XCSwiftPackageProductDependency; + package = 468DE06F27D28D4700F2D94B /* XCRemoteSwiftPackageReference "analytics-swift-mixpanel" */; + productName = SegmentMixpanel; + }; 4694D2F527B58C9600D110C0 /* FlurryAnalyticsSPM */ = { isa = XCSwiftPackageProductDependency; package = 4694D2F427B58C9500D110C0 /* XCRemoteSwiftPackageReference "FlurrySwiftPackage" */; @@ -577,21 +594,11 @@ package = 4694D2F727B58E5E00D110C0 /* XCRemoteSwiftPackageReference "comscore-swift-package-manager" */; productName = ComScore; }; - 4694D2FB27B5902100D110C0 /* Mixpanel */ = { - isa = XCSwiftPackageProductDependency; - package = 4694D2FA27B5902100D110C0 /* XCRemoteSwiftPackageReference "mixpanel-swift" */; - productName = Mixpanel; - }; 4694D2FE27B5914200D110C0 /* Intercom */ = { isa = XCSwiftPackageProductDependency; package = 4694D2FD27B5914200D110C0 /* XCRemoteSwiftPackageReference "intercom-ios" */; productName = Intercom; }; - 4694D30227B5979100D110C0 /* FacebookCore */ = { - isa = XCSwiftPackageProductDependency; - package = 4694D2F327B4500700D110C0 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */; - productName = FacebookCore; - }; 469F7B1C266011D70038E773 /* Segment */ = { isa = XCSwiftPackageProductDependency; productName = Segment; @@ -601,16 +608,6 @@ package = 469F7B21266013100038E773 /* XCRemoteSwiftPackageReference "ios_sdk" */; productName = Adjust; }; - 965DC1202669942800DDF9C7 /* FirebaseAnalytics */ = { - isa = XCSwiftPackageProductDependency; - package = 965DC11F2669942800DDF9C7 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; - productName = FirebaseAnalytics; - }; - BA384C9726824F3700AFEA1B /* AppsFlyerLib */ = { - isa = XCSwiftPackageProductDependency; - package = BA384C9626824F3700AFEA1B /* XCRemoteSwiftPackageReference "AppsFlyerFramework" */; - productName = AppsFlyerLib; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 469F7AFC266011690038E773 /* Project object */; diff --git a/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift b/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift index 4a884362..9deff949 100644 --- a/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift +++ b/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift @@ -7,6 +7,11 @@ import UIKit import Segment +import SegmentAmplitude +import SegmentAppsFlyer +import SegmentFacebook +import SegmentFirebase +import SegmentMixpanel @main class AppDelegate: UIResponder, UIApplicationDelegate { diff --git a/Examples/destination_plugins/AmplitudeSession.swift b/Examples/destination_plugins/AmplitudeSession.swift deleted file mode 100644 index 1c10ac90..00000000 --- a/Examples/destination_plugins/AmplitudeSession.swift +++ /dev/null @@ -1,167 +0,0 @@ -// -// AmplitudeSession.swift -// DestinationsExample -// -// Created by Cody Garvin on 2/16/21. -// - -// NOTE: This Plugin replicates Amplitude's session tracking functionality. -// It should be used to send session data to Amplitude via a cloud mode -// connection. Once implemented, the Amplitude SDK can be removed from -// your application. - -// NOTE: You can see this plugin in use in the DestinationsExample application. -// -// This plugin is NOT SUPPORTED by Segment. It is here merely as an example, -// and for your convenience should you find it useful. -// - -// MIT License -// -// Copyright (c) 2021 Segment -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import Segment - -class AmplitudeSession: EventPlugin, iOSLifecycle { - var key = "Actions Amplitude" - var type = PluginType.enrichment - var analytics: Analytics? - - var active = false - - private var sessionTimer: Timer? - private var sessionID: TimeInterval? - private let fireTime = TimeInterval(300) - - func update(settings: Settings, type: UpdateType) { - if settings.hasIntegrationSettings(key: key) { - active = true - } else { - active = false - } - } - - func execute(event: T?) -> T? { - if !active { - return event - } - - var result: T? = event - switch result { - case let r as IdentifyEvent: - result = self.identify(event: r) as? T - case let r as TrackEvent: - result = self.track(event: r) as? T - case let r as ScreenEvent: - result = self.screen(event: r) as? T - case let r as AliasEvent: - result = self.alias(event: r) as? T - case let r as GroupEvent: - result = self.group(event: r) as? T - default: - break - } - return result - } - - func track(event: TrackEvent) -> TrackEvent? { - guard let returnEvent = insertSession(event: event) as? TrackEvent else { - return nil - } - return returnEvent - } - - func identify(event: IdentifyEvent) -> IdentifyEvent? { - guard let returnEvent = insertSession(event: event) as? IdentifyEvent else { - return nil - } - return returnEvent - } - - func alias(event: AliasEvent) -> AliasEvent? { - guard let returnEvent = insertSession(event: event) as? AliasEvent else { - return nil - } - return returnEvent - } - - func screen(event: ScreenEvent) -> ScreenEvent? { - guard let returnEvent = insertSession(event: event) as? ScreenEvent else { - return nil - } - return returnEvent - } - - func group(event: GroupEvent) -> GroupEvent? { - guard let returnEvent = insertSession(event: event) as? GroupEvent else { - return nil - } - return returnEvent - } - - func applicationWillEnterForeground(application: UIApplication?) { - startTimer() - analytics?.log(message: "Amplitude Session ID: \(sessionID ?? -1)") - } - - func applicationWillResignActive(application: UIApplication?) { - stopTimer() - } -} - - -// MARK: - AmplitudeSession Helper Methods -extension AmplitudeSession { - func insertSession(event: RawEvent) -> RawEvent { - var returnEvent = event - if var integrations = event.integrations?.dictionaryValue, - let sessionID = sessionID { - - integrations[key] = ["session_id": (Int(sessionID) * 1000)] - returnEvent.integrations = try? JSON(integrations as Any) - } - return returnEvent - } - - @objc - func handleTimerFire(_ timer: Timer) { - stopTimer() - startTimer() - } - - func startTimer() { - sessionTimer = Timer(timeInterval: fireTime, target: self, - selector: #selector(handleTimerFire(_:)), - userInfo: nil, repeats: true) - sessionTimer?.tolerance = 0.3 - sessionID = Date().timeIntervalSince1970 - if let sessionTimer = sessionTimer { - // Use the RunLoop current to avoid retaining self - RunLoop.current.add(sessionTimer, forMode: .common) - } - } - - func stopTimer() { - sessionTimer?.invalidate() - sessionID = -1 - } -} diff --git a/Examples/destination_plugins/AppsFlyerDestination.swift b/Examples/destination_plugins/AppsFlyerDestination.swift deleted file mode 100644 index c0ef30a8..00000000 --- a/Examples/destination_plugins/AppsFlyerDestination.swift +++ /dev/null @@ -1,348 +0,0 @@ -// -// AppsFlyerDestination.swift -// -// Created by Alan Charles on 6/22/21. -// -// NOTE: You can see this plugin in use in the DestinationsExample application. -// -// This plugin is NOT SUPPORTED by Segment. It is here merely as an example, -// and for your convenience should you find it useful. -// -// AppsFlyer SPM package can be found here: https://github.com/adjust/ios_sdk -// MIT License -// -// Copyright (c) 2021 Segment -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// *** To Implement Deep Linking functionality reference: https://support.appsflyer.com/hc/en-us/articles/208874366 **** - -import Foundation -import UIKit -import Segment -import AppsFlyerLib - -@objc -class AppsFlyerDestination: UIResponder, DestinationPlugin { - let timeline = Timeline() - let type = PluginType.destination - let key = "AppsFlyer" - - var analytics: Analytics? - - fileprivate var settings: AppsFlyerSettings? = nil - - public func update(settings: Settings, type: UpdateType) { - // we've already set up this singleton SDK, can't do it again, so skip. - guard type == .initial else { return } - - guard let settings: AppsFlyerSettings = settings.integrationSettings(forPlugin: self) else { return } - self.settings = settings - - AppsFlyerLib.shared().appsFlyerDevKey = settings.appsFlyerDevKey - AppsFlyerLib.shared().appleAppID = settings.appleAppID - - AppsFlyerLib.shared().waitForATTUserAuthorization(timeoutInterval: 60) //OPTIONAL - AppsFlyerLib.shared().isDebug = true //OPTIONAL - AppsFlyerLib.shared().deepLinkDelegate = self //OPTIONAL - - let trackAttributionData = settings.trackAttributionData - - if trackAttributionData ?? false { - AppsFlyerLib.shared().delegate = self - } - } - - public func identify(event: IdentifyEvent) -> IdentifyEvent? { - if let userId = event.userId, userId.count > 0 { - AppsFlyerLib.shared().customerUserID = userId - } - - if let traits = event.traits?.dictionaryValue { - var aFTraits: [AnyHashable: Any] = [:] - - if let email = traits["email"] as? String { - aFTraits["email"] = email - } - - if let firstName = traits["firstName"] as? String { - aFTraits["firstName"] = firstName - } - - if let lastName = traits["lastName"] as? String { - aFTraits["lastName"] = lastName - } - - if traits["currencyCode"] != nil { - AppsFlyerLib.shared().currencyCode = traits["currencyCode"] as? String - } - - AppsFlyerLib.shared().customData = aFTraits - } - - return event - } - - public func track(event: TrackEvent) -> TrackEvent? { - - var properties = event.properties?.dictionaryValue - - let revenue: Double? = extractRevenue(key: "revenue", from: properties) - let currency: String? = extractCurrency(key: "currency", from: properties, withDefault: "USD") - - if let afRevenue = revenue, let afCurrency = currency { - properties?["af_revenue"] = afRevenue - properties?["af_currency"] = afCurrency - - properties?.removeValue(forKey: "revenue") - properties?.removeValue(forKey: "currency") - - AppsFlyerLib.shared().logEvent(event.event, withValues: properties) - - } else { - AppsFlyerLib.shared().logEvent(event.event, withValues: properties) - } - - return event - } -} - -extension AppsFlyerDestination: RemoteNotifications, iOSLifecycle { - func applicationDidBecomeActive(application: UIApplication?) { - AppsFlyerLib.shared().start() - } - - func openURL(_ url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) { - AppsFlyerLib.shared().handleOpen(url, options: options) - } - - func receivedRemoteNotification(userInfo: [AnyHashable: Any]) { - AppsFlyerLib.shared().handlePushNotification(userInfo) - } -} - -//MARK: - UserActivities Protocol - -extension AppsFlyerDestination: UserActivities { - func continueUserActivity(_ activity: NSUserActivity) { - AppsFlyerLib.shared().continue(activity, restorationHandler: nil) - } -} - - -//MARK: - Support methods -// matches existing AppsFlyer Destination to set revenue and currency as reserved properties -// https://github.com/AppsFlyerSDK/segment-appsflyer-ios/blob/master/segment-appsflyer-ios/Classes/SEGAppsFlyerIntegration.m#L148 -extension AppsFlyerDestination { - internal func extractRevenue(key: String, from properties: [String: Any]?) -> Double? { - - guard let revenueProperty = properties?[key] as? Double else {return nil} - - if let revenue = properties?["revenue"] as? String { - let revenueProperty = Double(revenue) - return revenueProperty - - } - return revenueProperty - } - - - internal func extractCurrency(key: String, from properties: [String: Any]?, withDefault value: String? = nil) -> String? { - - if let currency = properties?[key] as? String { - return currency - } - - return "USD" - } - -} - -// MARK: - AppsFlyer Lib Delegate conformance - -extension AppsFlyerDestination: AppsFlyerLibDelegate { - func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) { - guard let firstLaunchFlag = conversionInfo["is_first_launch"] as? Int else { - return - } - - guard let status = conversionInfo["af_status"] as? String else { - return - } - - if (firstLaunchFlag == 1) { - if (status == "Non-organic") { - if let mediaSource = conversionInfo["media_source"] , let campaign = conversionInfo["campaign"]{ - - let campaign: [String: Any] = [ - "source": mediaSource, - "name": campaign - ] - - let properties: [String: Any] = [ - "provider": "AppsFlyer", - "campaign": campaign - ] - - analytics?.track(name: "Install Attributed", properties: properties) - - } - } else { - analytics?.track(name: "Organic Install") - } - } else { - analytics?.log(message: "Not First Launch") - } - - } - - func onConversionDataFail(_ error: Error) { - analytics?.log(message: "\(error)") - } - - - func onAppOpenAttribution(_ attributionData: [AnyHashable: Any]) { - - if let media_source = attributionData["media_source"] , let campaign = attributionData["campaign"], - let referrer = attributionData["http_referrer"] { - - let campaign: [String: Any] = [ - "source": media_source, - "name": campaign, - "url": referrer - ] - - let properties: [String: Any] = [ - "provider": "AppsFlyer", - "campaign": campaign - ] - - analytics?.track(name: "Deep Link Opened", properties: properties) - } - } - - - func onAppOpenAttributionFailure(_ error: Error) { - analytics?.log(message: "\(error)") - } -} - -//MARK: - AppsFlyer DeepLink Delegate conformance - -extension AppsFlyerDestination: DeepLinkDelegate, UIApplicationDelegate { - - func didResolveDeepLink(_ result: DeepLinkResult) { - analytics?.log(message: "AppsFlyer Deeplink Result: \(result)") - switch result.status { - case .notFound: - analytics?.log(message: "AppsFlyer: Deep link not found") - return - case .failure: - analytics?.log(message: "AppsFlyer: Deep link failure!") - return - case .found: - analytics?.log(message: "AppsFlyer Deep link found") - } - - guard let deepLinkObj:DeepLink = result.deepLink else { return } - - if (deepLinkObj.isDeferred == true) { - - let campaign: [String: Any] = [ - "source": deepLinkObj.mediaSource ?? "", - "name": deepLinkObj.campaign ?? "", - "product": deepLinkObj.deeplinkValue ?? "" - ] - - let properties: [String: Any] = [ - "provider": "AppsFlyer", - "campaign": campaign - ] - - analytics?.track(name: "Deferred Deep Link", properties: properties) - - } else { - - let campaign: [String: Any] = [ - "source": deepLinkObj.mediaSource ?? "", - "name": deepLinkObj.campaign ?? "", - "product": deepLinkObj.deeplinkValue ?? "" - ] - - let properties: [String: Any] = [ - "provider": "AppsFlyer", - "campaign": campaign - ] - - analytics?.track(name: "Direct Deep Link", properties: properties) - - } - - // Uncomment the following code and alter to fit your implementation in order - // to collect deep linking attribution data - - //Logic to grab AppsFlyer's deep link value to instantiate correct VC - // guard let productNameStr = deepLinkObj.deeplinkValue else { - // print("Could not extract deep_link_value from deep link object") - // return - // } - - //implement your own logic to open the correct screen/content - // walkToSceneWithParams(product: productNameStr, deepLinkObj: deepLinkObj) - } - - - // User logic for opening Deep Links - // fileprivate func walkToSceneWithParams(product: String, deepLinkObj: DeepLink) { - // let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) - // UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true, completion: nil) - // - // let destVC = "product_vc" - // if let newVC = storyBoard.instantiateVC(withIdentifier: destVC) { - // - // print("[AFSDK] AppsFlyer routing to section: \(destVC)") - // newVC.deepLinkData = deepLinkObj - // - // UIApplication.shared.windows.first?.rootViewController?.present(newVC, animated: true, completion: nil) - // } else { - // print("[AFSDK] AppsFlyer: could not find section: \(destVC)") - // } - // } -} - -//MARK: - UI StoryBoard Extension; Deep Linking - -//Aditonal logic for Deep Linking -//extension UIStoryboard { -// func instantiateVC(withIdentifier identifier: String) -> DLViewController? { -// // "identifierToNibNameMap" – dont change it. It is a key for searching IDs -// if let identifiersList = self.value(forKey: "identifierToNibNameMap") as? [String: Any] { -// if identifiersList[identifier] != nil { -// return self.instantiateViewController(withIdentifier: identifier) as? DLViewController -// } -// } -// return nil -// } -//} - -private struct AppsFlyerSettings: Codable { - let appsFlyerDevKey: String - let appleAppID: String - let trackAttributionData: Bool? -} diff --git a/Examples/destination_plugins/FacebookAppEventsDestination.swift b/Examples/destination_plugins/FacebookAppEventsDestination.swift deleted file mode 100644 index 54b97b54..00000000 --- a/Examples/destination_plugins/FacebookAppEventsDestination.swift +++ /dev/null @@ -1,180 +0,0 @@ -// -// FacebookAppEventsDestination.swift -// -// -// Created by Brandon Sneed on 2/9/22. -// - -import Foundation -import Segment -import FBSDKCoreKit - -/** - An implementation of the Facebook App Events Analytics device mode destination - as a plugin. - */ - -// NOTE: You can see this plugin in use in the DestinationsExample application. -// -// This plugin is NOT SUPPORTED by Segment. It is here merely as an example, -// and for your convenience should you find it useful. -// -// Copyright (c) 2022 Segment -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - - -class FacebookAppEventsDestination: DestinationPlugin, iOSLifecycle { - typealias FBSettings = FBSDKCoreKit.Settings - - let timeline = Timeline() - let type = PluginType.destination - let key = "Facebook App Events" - var analytics: Analytics? = nil - - var dpOptions = ["LDU"] - var dpCountry: Int32 = 0 - var dpState: Int32 = 0 - - static let formatter = NumberFormatter() - - private var settings: FacebookAppEventsSettings? = nil - - init() { - // creating formatters are expensive, so we make it static - // and just set the style later. - Self.formatter.numberStyle = .decimal - } - - public func update(settings: Segment.Settings, type: UpdateType) { - // we've already set up this singleton SDK, can't do it again, so skip. - guard type == .initial else { return } - - guard let settings: FacebookAppEventsSettings = settings.integrationSettings(forPlugin: self) else { return } - self.settings = settings - - FBSettings.shared.appID = settings.appId - if let ldu = settings.limitedDataUse, ldu { - FBSettings.shared.setDataProcessingOptions(dpOptions, country: dpCountry, state: dpState) - } else { - FBSettings.shared.setDataProcessingOptions(nil) - } - } - - func track(event: TrackEvent) -> TrackEvent? { - // FB Event Names must be <= 40 characters - let truncatedEventName = AppEvents.Name(String(event.event.prefix(40))) - - let revenue = extractRevenue(properties: event.properties, key: "revenue") - let currency = extractCurrency(properties: event.properties, key: "currency") - - var params = extractParameters(properties: event.properties) - - if let revenue = revenue { - params[AppEvents.ParameterName.currency] = currency - - AppEvents.shared.logEvent(truncatedEventName, valueToSum: revenue, parameters: params) - AppEvents.shared.logPurchase(amount: revenue, currency: currency, parameters: params as? [String: Any]) - } else { - AppEvents.shared.logEvent(truncatedEventName, parameters: params) - } - - return event - } - - func screen(event: ScreenEvent) -> ScreenEvent? { - // FB Event Names must be <= 40 characters - // 'Viewed' and 'Screen' with spaces take up 14 - let truncatedEventName = String((event.name ?? "").prefix(26)) - let newEventName = "Viewed \(truncatedEventName) Screen" - AppEvents.shared.logEvent(AppEvents.Name(newEventName)) - return event - } - - func applicationDidBecomeActive(application: UIApplication?) { - ApplicationDelegate.shared.initializeSDK() - } -} - -// MARK: Helper methods - -extension FacebookAppEventsDestination { - func extractParameters(properties: JSON?) -> [AppEvents.ParameterName: Any] { - // Facebook only accepts properties/parameters that have an NSString key, and an NSString or NSNumber as a value. - // ... so we need to strip out everything else. Not doing so results in a refusal to send and an - // error in the console from the FBSDK. - var result = [AppEvents.ParameterName: Any]() - guard let properties = properties?.dictionaryValue else { return result } - - for (key, value) in properties { - switch value { - case let v as NSString: - result[AppEvents.ParameterName(key)] = v - case let v as NSNumber: - result[AppEvents.ParameterName(key)] = v - default: - break - } - } - - return result - } - - func extractRevenue(properties: JSON?, key: String) -> Double? { - guard let dict = properties?.dictionaryValue else { return nil } - - var revenue: Any? = nil - for revenueKey in dict.keys { - if key.caseInsensitiveCompare(revenueKey) == .orderedSame { - revenue = dict[revenueKey] - } - } - - if revenue is String, let revenue = revenue as? String { - // format the revenue - let result = Self.formatter.number(from: revenue) as? Double - return result - } else if revenue is Double { - return revenue as? Double - } - - return nil - } - - func extractCurrency(properties: JSON?, key: String) -> String { - var result = "USD" - guard let dict = properties?.dictionaryValue else { return result } - let found = dict.keys.filter { dictKey in - return (key.caseInsensitiveCompare(dictKey) == .orderedSame) - } - if let key = found.first, let value = dict[key] as? String { - result = value - } - return result - } -} - -// MARK: Settings - -private struct FacebookAppEventsSettings: Codable { - let appId: String - let limitedDataUse: Bool? -} - diff --git a/Examples/destination_plugins/FirebaseDestination.swift b/Examples/destination_plugins/FirebaseDestination.swift deleted file mode 100644 index c131a45d..00000000 --- a/Examples/destination_plugins/FirebaseDestination.swift +++ /dev/null @@ -1,228 +0,0 @@ -// -// FirebaseDestination.swift -// DestinationsExample -// -// Created by Cody Garvin on 6/3/21. -// - -// NOTE: You can see this plugin in use in the DestinationsExample application. -// -// This plugin is NOT SUPPORTED by Segment. It is here merely as an example, -// and for your convenience should you find it useful. -// -// Firebase SPM package can be found here: https://github.com/firebase/firebase-ios-sdk - -// MIT License -// -// Copyright (c) 2021 Segment -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import Segment -import Firebase -import FirebaseAnalytics - -/** - An implmentation of the Firebase Analytics device mode destination as a plugin. - */ - -class FirebaseDestination: DestinationPlugin { - let timeline = Timeline() - let type = PluginType.destination - let key = "Firebase" - var analytics: Segment.Analytics? = nil - - func update(settings: Settings, type: UpdateType) { - // we've already set up this singleton SDK, can't do it again, so skip. - guard type == .initial else { return } - - guard let firebaseSettings: FirebaseSettings = settings.integrationSettings(forPlugin: self) else { return } - if let deepLinkURLScheme = firebaseSettings.deepLinkURLScheme { - FirebaseOptions.defaultOptions()?.deepLinkURLScheme = deepLinkURLScheme - analytics?.log(message: "Added deepLinkURLScheme: \(deepLinkURLScheme)") - } - - // First check if firebase has been set up from a previous settings call - if (FirebaseApp.app() != nil) { - analytics?.log(message: "Firebase already configured, skipping") - } else { - FirebaseApp.configure() - } - } - - func identify(event: IdentifyEvent) -> IdentifyEvent? { - - if let userId = event.userId { - FirebaseAnalytics.Analytics.setUserID(userId) - analytics?.log(message: "Firebase setUserId(\(userId))") - } - - // Check the user properties for type - if let traits = event.traits, - let mapDictionary = traits.dictionaryValue { - // Send off to identify - mapToStrings(mapDictionary) { key, data in - FirebaseAnalytics.Analytics.setUserProperty(data, forName: key) - analytics?.log(message: "Firebase setUserPropertyString \(data) for key \(key)") - } - } - - return event - } - - func track(event: TrackEvent) -> TrackEvent? { - - let name = formatFirebaseEventNames(event.event) - var parameters: [String: Any]? = nil - if let properties = event.properties?.dictionaryValue { - parameters = returnMappedFirebaseParameters(properties) - } - - FirebaseAnalytics.Analytics.logEvent(name, parameters: parameters) - analytics?.log(message: "Firebase logEventWithName \(name) parameters \(String(describing: parameters))") - return event - } - - func screen(event: ScreenEvent) -> ScreenEvent? { - - if let eventName = event.name { - FirebaseAnalytics.Analytics.logEvent(FirebaseAnalytics.AnalyticsEventScreenView, - parameters: [FirebaseAnalytics.AnalyticsParameterScreenName: eventName]) - analytics?.log(message: "Firebase setScreenName \(eventName)") - } - - - return event - } -} - -// MARK: - Support methods - -extension FirebaseDestination { - - // Maps Segment spec to Firebase constant - func formatFirebaseEventNames(_ eventName: String) -> String { - - if let mappedEvent = FirebaseDestination.mappedValues[eventName] { - return mappedEvent - } else { - return (try? formatFirebaseName(eventName)) ?? eventName - } - } - - func formatFirebaseName(_ eventName: String) throws -> String { - let trimmed = eventName.trimmingCharacters(in: .whitespaces) - do { - let regex = try NSRegularExpression(pattern: "([^a-zA-Z0-9_])", options: .caseInsensitive) - let formattedString = regex.stringByReplacingMatches(in: trimmed, options: .reportProgress, range: NSMakeRange(0, trimmed.count), withTemplate: "_") - - // Resize the string to maximum 40 characters if needed - let range = NSRange(location: 0, length: min(formattedString.count, 40)) - return NSString(string: formattedString).substring(with: range) - } catch { - analytics?.log(message: "Could not parse event name for Firebase.") - throw(error) - } - } - - func returnMappedFirebaseParameters(_ properties: [String: Any]) -> [String: Any] { - - - var mappedValues = properties - - for (key, firebaseKey) in FirebaseDestination.mappedKeys { - if var data = properties[key] { - - mappedValues.removeValue(forKey: key) - - if let castData = data as? [String: Any] { - data = returnMappedFirebaseParameters(castData) - } else if let castArray = data as? [Any] { - var updatedArray = [Any]() - for item in castArray { - if let castDictionary = item as? [String: Any] { - updatedArray.append(returnMappedFirebaseParameters(castDictionary)) - } else { - updatedArray.append(item) - } - } - data = updatedArray - } - - // Check key name for proper format - if let updatedFirebaseKey = try? formatFirebaseName(firebaseKey) { - mappedValues[updatedFirebaseKey] = data - } - } - } - - return mappedValues - } - - // Makes sure all traits are string based for Firebase API - func mapToStrings(_ mapDictionary: [String: Any], finalize: (String, String) -> Void) { - - for (key, data) in mapDictionary { - var dataString = data as? String ?? "\(data)" - let keyString = key.replacingOccurrences(of: " ", with: "_") - dataString = dataString.trimmingCharacters(in: .whitespacesAndNewlines) - finalize(keyString, dataString) - } - } -} - - -private struct FirebaseSettings: Codable { - let deepLinkURLScheme: String? -} - -private extension FirebaseDestination { - - static let mappedValues = ["Product Clicked": FirebaseAnalytics.AnalyticsEventSelectContent, - "Product Viewed": FirebaseAnalytics.AnalyticsEventViewItem, - "Product Added": FirebaseAnalytics.AnalyticsEventAddToCart, - "Product Removed": FirebaseAnalytics.AnalyticsEventRemoveFromCart, - "Checkout Started": FirebaseAnalytics.AnalyticsEventBeginCheckout, - "Promotion Viewed": FirebaseAnalytics.AnalyticsEventPresentOffer, - "Payment Info Entered": FirebaseAnalytics.AnalyticsEventAddPaymentInfo, - "Order Completed": FirebaseAnalytics.AnalyticsEventPurchase, - "Order Refunded": FirebaseAnalytics.AnalyticsEventRefund, - "Product List Viewed": FirebaseAnalytics.AnalyticsEventViewItemList, - "Product Added to Wishlist": FirebaseAnalytics.AnalyticsEventAddToWishlist, - "Product Shared": FirebaseAnalytics.AnalyticsEventShare, - "Cart Shared": FirebaseAnalytics.AnalyticsEventShare, - "Products Searched": FirebaseAnalytics.AnalyticsEventSearch] - - static let mappedKeys = ["products": FirebaseAnalytics.AnalyticsParameterItems, - "category": FirebaseAnalytics.AnalyticsParameterItemCategory, - "product_id": FirebaseAnalytics.AnalyticsParameterItemID, - "name": FirebaseAnalytics.AnalyticsParameterItemName, - "brand": FirebaseAnalytics.AnalyticsParameterItemBrand, - "price": FirebaseAnalytics.AnalyticsParameterPrice, - "quantity": FirebaseAnalytics.AnalyticsParameterQuantity, - "query": FirebaseAnalytics.AnalyticsParameterSearchTerm, - "shipping": FirebaseAnalytics.AnalyticsParameterShipping, - "tax": FirebaseAnalytics.AnalyticsParameterTax, - "total": FirebaseAnalytics.AnalyticsParameterValue, - "revenue": FirebaseAnalytics.AnalyticsParameterValue, - "order_id": FirebaseAnalytics.AnalyticsParameterTransactionID, - "currency": FirebaseAnalytics.AnalyticsParameterCurrency] - -} diff --git a/Examples/destination_plugins/MixpanelDestination.swift b/Examples/destination_plugins/MixpanelDestination.swift deleted file mode 100644 index 6de56ac4..00000000 --- a/Examples/destination_plugins/MixpanelDestination.swift +++ /dev/null @@ -1,342 +0,0 @@ -// -// MixpanelDestination.swift -// DestinationsExample -// -// Created by Cody Garvin on 1/15/21. -// - -// NOTE: You can see this plugin in use in the DestinationsExample application. -// -// This plugin is NOT SUPPORTED by Segment. It is here merely as an example, -// and for your convenience should you find it useful. -// -// Mixpanel SPM package can be found here: https://github.com/mixpanel/mixpanel-swift - -// MIT License -// -// Copyright (c) 2021 Segment -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import Segment -import Mixpanel - -class MixpanelDestination: DestinationPlugin, RemoteNotifications { - let timeline = Timeline() - let type = PluginType.destination - let key = "Mixpanel" - var analytics: Analytics? = nil - - private var mixpanel: MixpanelInstance? = nil - private var mixpanelSettings: MixpanelSettings? = nil - - func update(settings: Settings, type: UpdateType) { - // we've already set up this singleton SDK, can't do it again, so skip. - guard type == .initial else { return } - - // If we have a mixpanel instance, dump all the data first - mixpanel?.flush() - - // TODO: Update the proper types - guard let tempSettings: MixpanelSettings = settings.integrationSettings(forPlugin: self) else { - mixpanel = nil - analytics?.log(message: "Could not load Mixpanel settings") - return - } - - mixpanelSettings = tempSettings - - // Initialize mixpanel - if let token = mixpanelSettings?.token { - mixpanel = Mixpanel.initialize(token: token) - } - - // Change the endpoint if euro one is set - if let euEndpointEnabled = mixpanelSettings?.enableEuropeanUnionEndpoint, - euEndpointEnabled { - mixpanel?.serverURL = "api-eu.mixpanel.com" - } - } - - func identify(event: IdentifyEvent) -> IdentifyEvent? { - // Ensure that the userID is set and valid - if let eventUserID = event.userId, !eventUserID.isEmpty { - mixpanel?.identify(distinctId: eventUserID) - analytics?.log(message: "Mixpanel identify \(eventUserID)") - } - - guard let traits = try? event.traits?.dictionaryValue?.mapTransform(MixpanelDestination.keyMap, - valueTransform: nil) as? Properties else { - return event - } - - if setAllTraitsByDefault() { - - // Register the mapped traits - mixpanel?.registerSuperProperties(traits) - analytics?.log(message: "Mixpanel registerSuperProperties \(traits)") - - // Mixpanel also has a people API that works separately so we set hte traits for it as well. - if peopleEnabled() { - mixpanel?.people.set(properties: traits) - analytics?.log(message: "Mixpanel people set \(traits)") - } - } - - if let superProperties = mixpanelSettings?.superProperties { - var superPropertyTraits = [String: Any]() - for superProperty in superProperties { - superPropertyTraits[superProperty] = traits[superProperty] - } - guard let mappedSuperProperties = try? superPropertyTraits.mapTransform(MixpanelDestination.keyMap, - valueTransform: nil) as? [String: MixpanelType] else { - return event - } - - mixpanel?.registerSuperProperties(mappedSuperProperties) - analytics?.log(message: "Mixpanel registerSuperProperties \(mappedSuperProperties)") - - if peopleEnabled(), let peopleProperties = mixpanelSettings?.peopleProperties { - var peoplePropertyTraits = [String: Any]() - for peopleProperty in peopleProperties { - peoplePropertyTraits[peopleProperty] = traits[peopleProperty] - } - guard let mappedPeopleProperties = try? peoplePropertyTraits.mapTransform(MixpanelDestination.keyMap, - valueTransform: nil) as? [String: MixpanelType] else { - return event - } - mixpanel?.people.set(properties: mappedPeopleProperties) - analytics?.log(message: "Mixpanel people set \(mappedSuperProperties)") - } - } - - return event - } - - func track(event: TrackEvent) -> TrackEvent? { - mixpanelTrack(event.event, properties: event.properties?.dictionaryValue) - return event - } - - func screen(event: ScreenEvent) -> ScreenEvent? { - if mixpanelSettings?.consolidatedPageCalls ?? false, - var payloadProps = event.properties?.dictionaryValue { - - let eventName = "Loaded a Screen" - if let name = event.name { - payloadProps["name"] = name - } - mixpanelTrack(eventName, properties: payloadProps) - analytics?.log(message: "Mixpanel track \(eventName) properties \(payloadProps)") - } else if mixpanelSettings?.trackAllPages ?? false { - - var finalEventName = "Viewed Screen" - if let eventName = event.name { - finalEventName = "Viewed \(eventName) Screen" - } - - mixpanelTrack(finalEventName, properties: event.properties?.dictionaryValue) - analytics?.log(message: "Mixpanel track \(finalEventName) properties \(String(describing: event.properties?.dictionaryValue))") - } else if mixpanelSettings?.trackNamedPages ?? false, let eventName = event.name { - let finalEventName = "Viewed \(eventName) Screen" - mixpanelTrack(finalEventName, properties: event.properties?.dictionaryValue) - analytics?.log(message: "Mixpanel track \(finalEventName) properties \(String(describing: event.properties?.dictionaryValue))") - } else if mixpanelSettings?.trackCategorizedPages ?? false, let category = event.category { - let finalEventName = "Viewed \(category) Screen" - mixpanelTrack(finalEventName, properties: event.properties?.dictionaryValue) - analytics?.log(message: "Mixpanel track \(finalEventName) properties \(String(describing: event.properties?.dictionaryValue))") - } - - return event - } - - func group(event: GroupEvent) -> GroupEvent? { - - guard let groupID = event.groupId, !groupID.isEmpty, - let groupIdentifierProperties = mixpanelSettings?.groupIdentifierTraits else { - return event - } - - // No need to continue if we don't have any properties - if groupIdentifierProperties.isEmpty { - return event - } - - for key in groupIdentifierProperties { - let nsGroupID = NSString(string: groupID) - - if let traits = event.traits?.dictionaryValue as? Properties { - if let group = traits[key] as? String { - mixpanel?.getGroup(groupKey: group, groupID: nsGroupID).setOnce(properties: traits) - } - } - - mixpanel?.setGroup(groupKey: key, groupID: nsGroupID) - analytics?.log(message: "Mixpanel setGroup \(key) groupID \(groupID)") - } - - return event - } - - func alias(event: AliasEvent) -> AliasEvent? { - // Use Mixpanel generated id - if let distinctId = mixpanel?.distinctId, let newId = event.userId { - mixpanel?.createAlias(newId, distinctId: distinctId) - } - return event - } - - func reset() { - flush() - - mixpanel?.reset() - analytics?.log(message: "Mixpanel reset") - } - - func flush() { - mixpanel?.flush() - analytics?.log(message: "Mixpanel Flush") - } -} - -// MARK: - Mixpanel Helper Methods -extension MixpanelDestination { - - private func mixpanelTrack(_ eventName: String, properties: [String: Any]? = nil) { - // Track the raw event - let typedProperties: Properties? = properties as? Properties ?? nil - mixpanel?.track(event: eventName, properties: typedProperties) - - // Don't send anything else to mixpanel if it is disabled - if !peopleEnabled() { - return - } - - - if let properties = properties { - // Increment properties that are listed in the Mixpanel integration settings - incrementProperties(properties) - - // Extract the revenue from the properties passed in to us - if let revenue = extractRevenue(properties, key: "revenue") { - mixpanel?.people.trackCharge(amount: revenue) - analytics?.log(message: "Mixpanel people trackCharge \(revenue)") - } - } - - if eventShouldIncrement(event: eventName) { - mixpanel?.people.increment(property: eventName, by: 1) - analytics?.log(message: "Mixpanel people increment \(eventName) by 1") - - let lastEvent = "Last \(eventName)" - let lastDate = Date() - mixpanel?.people.set(property: lastEvent, to: lastDate) - analytics?.log(message: "Mixpanel people set \(lastEvent) to \(lastDate)") - } - - } - - private func extractRevenue(_ properties: [String: Any], key: String) -> Double? { - var revenue: Double? = nil - if let revenueProperty = properties[key] { - if let revenueProperty = revenueProperty as? String { - revenue = Double(revenueProperty) - } else if let revenueProperty = revenueProperty as? NSNumber { - revenue = revenueProperty.doubleValue - } - } - - return revenue - } - - private func eventShouldIncrement(event: String) -> Bool { - var shouldIncrement = false - if let propertyIncrements = mixpanelSettings?.eventIncrements { - for increment in propertyIncrements { - if event.lowercased() == increment.lowercased() { - shouldIncrement = true - break - } - } - } - - return shouldIncrement - } - - private func incrementProperties(_ properties: [String: Any]) { - if let propertyIncrements = mixpanelSettings?.propIncrements { - for propString in propertyIncrements { - for property in properties.keys { - if propString.lowercased() == property.lowercased(), - let incrementValue = properties[property] as? Double { - mixpanel?.people.increment(property: property, by: incrementValue) - analytics?.log(message: "Mixpanel people increment \(property) by \(incrementValue)") - } - } - } - } - } - - private func peopleEnabled() -> Bool { - var enabled = false - if let peopleEnabled = mixpanelSettings?.people { - enabled = peopleEnabled - } - - return enabled - } - - private func setAllTraitsByDefault() -> Bool { - var traitsByDefault = false - if let setAllTraitsByDefault = mixpanelSettings?.setAllTraitsByDefault { - traitsByDefault = setAllTraitsByDefault - } - - return traitsByDefault - } -} - -private struct MixpanelSettings: Codable { - let token: String - let enableEuropeanUnionEndpoint: Bool - let consolidatedPageCalls: Bool - let trackAllPages: Bool - let trackNamedPages: Bool - let trackCategorizedPages: Bool - let people: Bool - let setAllTraitsByDefault: Bool - let superProperties: [String]? - let peopleProperties: [String]? - let groupIdentifierTraits: [String]? - let eventIncrements: [String]? - let propIncrements: [String]? -} - -private extension MixpanelDestination { - - static let keyMap = ["$first_name": "firstName", - "$last_name": "lastName", - "$created": "createdAt", - "$last_seen": "lastSeen", - "$email": "email", - "$name": "name", - "$username": "username", - "$phone": "phone"] -}