From eeced3eb1706019dc49823fd11149b89141a4e16 Mon Sep 17 00:00:00 2001 From: Andrey Sokolov Date: Thu, 28 Sep 2023 21:36:42 +0400 Subject: [PATCH] feat: Objective-C support (#84) * feat: Objective-C support * fix: Objective-C Example App/tests * fix: set iOS Deployment Target to 16.1 * fix: static functions to create ObjC instances * fix: mergeEventOptions() * fix: add more SDK usage * fix: fix recursion error in ObjCProperties.remove --------- Co-authored-by: justin.fiedler --- .github/workflows/release.yml | 7 + .github/workflows/unit-test.yml | 7 + Amplitude-Swift.xcodeproj/project.pbxproj | 76 ++- Examples/AmplitudeObjCExample/.gitignore | 1 + .../project.pbxproj | 528 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AmplitudeObjCExample/AppDelegate.h | 7 + .../AmplitudeObjCExample/AppDelegate.m | 85 +++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../Base.lproj/Main.storyboard | 44 ++ .../AmplitudeObjCExample/Info.plist | 25 + .../AmplitudeObjCExample/SceneDelegate.h | 8 + .../AmplitudeObjCExample/SceneDelegate.m | 50 ++ .../AmplitudeObjCExample/ViewController.h | 7 + .../AmplitudeObjCExample/ViewController.m | 15 + .../AmplitudeObjCExample/main.m | 11 + .../AmplitudeObjCExampleTests.m | 235 ++++++++ .../AmplitudeSwiftUIExampleApp.swift | 2 +- .../TroubleShootingPlugin.swift | 2 +- Sources/Amplitude/Configuration.swift | 10 +- Sources/Amplitude/Constants.swift | 26 +- Sources/Amplitude/Events/Identify.swift | 40 +- Sources/Amplitude/ObjC/ObjCAmplitude.swift | 209 +++++++ Sources/Amplitude/ObjC/ObjCBaseEvent.swift | 87 +++ .../Amplitude/ObjC/ObjCConfiguration.swift | 275 +++++++++ .../ObjC/ObjCDeepLinkOpenedEvent.swift | 38 ++ .../ObjC/ObjCDefaultTrackingOptions.swift | 55 ++ Sources/Amplitude/ObjC/ObjCEventOptions.swift | 398 +++++++++++++ Sources/Amplitude/ObjC/ObjCIdentify.swift | 97 ++++ .../ObjC/ObjCIngestionMetadata.swift | 35 ++ .../Amplitude/ObjC/ObjCLoggerProvider.swift | 42 ++ Sources/Amplitude/ObjC/ObjCPlan.swift | 55 ++ Sources/Amplitude/ObjC/ObjCPlugin.swift | 64 +++ Sources/Amplitude/ObjC/ObjCProperties.swift | 29 + Sources/Amplitude/ObjC/ObjCRevenue.swift | 95 ++++ .../ObjC/ObjCScreenViewedEvent.swift | 14 + Sources/Amplitude/ObjC/ObjCStorage.swift | 29 + .../Amplitude/ObjC/ObjCTrackingOptions.swift | 183 ++++++ Sources/Amplitude/TrackingOptions.swift | 16 +- Sources/Amplitude/Types.swift | 13 +- .../Utilities/CodableExtension.swift | 16 +- Tests/AmplitudeTests/AmplitudeTests.swift | 2 +- .../AmplitudeTests/Events/IdentifyTests.swift | 2 +- 47 files changed, 2987 insertions(+), 23 deletions(-) create mode 100644 Examples/AmplitudeObjCExample/.gitignore create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.pbxproj create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/AppDelegate.h create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/AppDelegate.m create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/Contents.json create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/Base.lproj/LaunchScreen.storyboard create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/Base.lproj/Main.storyboard create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/Info.plist create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/SceneDelegate.h create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/SceneDelegate.m create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/ViewController.h create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/ViewController.m create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExample/main.m create mode 100644 Examples/AmplitudeObjCExample/AmplitudeObjCExampleTests/AmplitudeObjCExampleTests.m create mode 100644 Sources/Amplitude/ObjC/ObjCAmplitude.swift create mode 100644 Sources/Amplitude/ObjC/ObjCBaseEvent.swift create mode 100644 Sources/Amplitude/ObjC/ObjCConfiguration.swift create mode 100644 Sources/Amplitude/ObjC/ObjCDeepLinkOpenedEvent.swift create mode 100644 Sources/Amplitude/ObjC/ObjCDefaultTrackingOptions.swift create mode 100644 Sources/Amplitude/ObjC/ObjCEventOptions.swift create mode 100644 Sources/Amplitude/ObjC/ObjCIdentify.swift create mode 100644 Sources/Amplitude/ObjC/ObjCIngestionMetadata.swift create mode 100644 Sources/Amplitude/ObjC/ObjCLoggerProvider.swift create mode 100644 Sources/Amplitude/ObjC/ObjCPlan.swift create mode 100644 Sources/Amplitude/ObjC/ObjCPlugin.swift create mode 100644 Sources/Amplitude/ObjC/ObjCProperties.swift create mode 100644 Sources/Amplitude/ObjC/ObjCRevenue.swift create mode 100644 Sources/Amplitude/ObjC/ObjCScreenViewedEvent.swift create mode 100644 Sources/Amplitude/ObjC/ObjCStorage.swift create mode 100644 Sources/Amplitude/ObjC/ObjCTrackingOptions.swift diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 837bc90f..6dc081d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,6 +57,13 @@ jobs: -sdk watchsimulator \ -destination 'platform=watchOS Simulator,name=Apple Watch Series 8 (41mm)' \ test + - name: Objective-C Example Tests (iOS) + working-directory: Examples/AmplitudeObjCExample + run: | + xcodebuild test \ + -scheme AmplitudeObjCExample \ + -sdk iphonesimulator \ + -destination 'platform=iOS Simulator,name=iPhone 14' - name: Validate Podfile run: pod lib lint --allow-warnings diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 233e4aed..0404bef3 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -47,3 +47,10 @@ jobs: -sdk watchsimulator \ -destination 'platform=watchOS Simulator,name=Apple Watch Series 8 (41mm)' \ test + - name: Objective-C Example Tests (iOS) + working-directory: Examples/AmplitudeObjCExample + run: | + xcodebuild test \ + -scheme AmplitudeObjCExample \ + -sdk iphonesimulator \ + -destination 'platform=iOS Simulator,name=iPhone 14' diff --git a/Amplitude-Swift.xcodeproj/project.pbxproj b/Amplitude-Swift.xcodeproj/project.pbxproj index ee4dd28f..63ababba 100644 --- a/Amplitude-Swift.xcodeproj/project.pbxproj +++ b/Amplitude-Swift.xcodeproj/project.pbxproj @@ -21,20 +21,36 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 8EDEC02B99EE2092B567A61D /* ObjCIngestionMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC500EBDA8B813056E2DB /* ObjCIngestionMetadata.swift */; }; 8EDEC1073A308B12B5CCD975 /* AnalyticsConnectorPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECD39BAA97DD4320C0AA5 /* AnalyticsConnectorPlugin.swift */; }; + 8EDEC10C56FA7F7DEEB48B6F /* ObjCBaseEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECCFD935A0C5A6FE85E87 /* ObjCBaseEvent.swift */; }; 8EDEC14255F82E24CEE00B36 /* AmplitudeSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC0630C3B587334275D9B /* AmplitudeSessionTests.swift */; }; + 8EDEC2155081F99515DF9C82 /* ObjCProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC18449A3E12FCADAD756 /* ObjCProperties.swift */; }; 8EDEC2E0CC80DF79F5463ACC /* RemnantDataMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC6A9899998F823C278F7 /* RemnantDataMigrationTests.swift */; }; 8EDEC30C0075E9D92B1B5210 /* UIKitScreenViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC650EF79B104DC3C9F4C /* UIKitScreenViews.swift */; }; 8EDEC3283B812D5D34DADF7B /* AnalyticsConnectorIdentityPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC48916EFEF6D5B3EEF9A /* AnalyticsConnectorIdentityPlugin.swift */; }; + 8EDEC43520B2DCF584F1035D /* ObjCScreenViewedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC7D924907683D0A14CA6 /* ObjCScreenViewedEvent.swift */; }; 8EDEC43FB30802F70112E577 /* ScreenViewedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECB455E34133AB256D720 /* ScreenViewedEvent.swift */; }; 8EDEC4D0C0CE07BF211804CC /* DefaultTrackingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC17A6A0F8112A5DC8CEB /* DefaultTrackingOptions.swift */; }; 8EDEC4EE0DE1C89889F451B5 /* QueueTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC4F83BFAA664749FAEF0 /* QueueTimeTests.swift */; }; 8EDEC51F746CC25D27E32F6A /* DeepLinkOpenedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECC405793463C10F52EF2 /* DeepLinkOpenedEvent.swift */; }; + 8EDEC5F7208B1C327C8703D7 /* ObjCStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECF8CF745F7339B65D6DB /* ObjCStorage.swift */; }; + 8EDEC74C71FEC9056DC7358F /* ObjCLoggerProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECD4B7ED7CBCD6955D097 /* ObjCLoggerProvider.swift */; }; 8EDEC8F8DD2CDCD6568512F8 /* RemnantDataMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC19F9FBC98A0D4E5A513 /* RemnantDataMigration.swift */; }; 8EDEC94F3562C2FACAA58A3D /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC54AB4DF9E1074C3D6A4 /* Weak.swift */; }; 8EDEC972AEB33E4528F7FEEB /* StoragePrefixMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC9B98272069D70D08EA4 /* StoragePrefixMigrationTests.swift */; }; 8EDEC977C03AA2676724F436 /* BasePlugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC448C42C8C0A464FAA15 /* BasePlugins.swift */; }; + 8EDEC98799DCB6610B8DBA19 /* ObjCAmplitude.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC1576C95A2EB2FEF00A8 /* ObjCAmplitude.swift */; }; + 8EDECA4DAFA67CD4785D0161 /* ObjCDefaultTrackingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC44D23EBD92D7892628E /* ObjCDefaultTrackingOptions.swift */; }; + 8EDECB800546E37719391E65 /* ObjCPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECB1FA2AFF022A19104EE /* ObjCPlan.swift */; }; + 8EDECBA91C61037025B85EFA /* ObjCRevenue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECB730131FFFBE11EF9FF /* ObjCRevenue.swift */; }; + 8EDECC1FC97DDF0BEFAA96E7 /* ObjCDeepLinkOpenedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECD6CF59CA94D5D0256CD /* ObjCDeepLinkOpenedEvent.swift */; }; + 8EDECD1E851511887BE000F9 /* ObjCEventOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECE07F682FAAE47F77B24 /* ObjCEventOptions.swift */; }; 8EDECD602E181B3E2E85D4DF /* StoragePrefixMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECC2335BCF4C2EC3A6206 /* StoragePrefixMigration.swift */; }; + 8EDECDF8E188CD7F24EEB475 /* ObjCTrackingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC48D9C242C1A39AABCC4 /* ObjCTrackingOptions.swift */; }; + 8EDECE3AA54253BA167E89CB /* ObjCPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC1160D95DC3F0E48DDF7 /* ObjCPlugin.swift */; }; + 8EDECEC5F98F9974DF3E576F /* ObjCIdentify.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC5A50E197C9C5067C19E /* ObjCIdentify.swift */; }; + 8EDECF81C2B1B38D472FD7EF /* ObjCConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECEC5AAE15FD05E76359A /* ObjCConfiguration.swift */; }; 8EDECFCCF4219767F26210D6 /* Sessions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC2B8B38E04CDB51F0E83 /* Sessions.swift */; }; BA0359CA2A51585D007C383B /* legacy_v3.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = BA0359C92A51585D007C383B /* legacy_v3.sqlite */; }; BA0639F62A4DD491000F1CEE /* LegacyDatabaseStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0639F52A4DD491000F1CEE /* LegacyDatabaseStorage.swift */; }; @@ -107,34 +123,50 @@ isa = PBXContainerItemProxy; containerPortal = OBJ_1 /* Project object */; proxyType = 1; - remoteGlobalIDString = "amplitude-swift::Amplitude-Swift"; + remoteGlobalIDString = amplitude-swift::Amplitude-Swift; remoteInfo = "Amplitude-Swift"; }; 580FD1F1294A56F60036777B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = OBJ_1 /* Project object */; proxyType = 1; - remoteGlobalIDString = "amplitude-swift::Amplitude-SwiftTests"; + remoteGlobalIDString = amplitude-swift::Amplitude-SwiftTests; remoteInfo = "Amplitude-SwiftTests"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 8EDEC0630C3B587334275D9B /* AmplitudeSessionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmplitudeSessionTests.swift; sourceTree = ""; }; + 8EDEC1160D95DC3F0E48DDF7 /* ObjCPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCPlugin.swift; sourceTree = ""; }; + 8EDEC1576C95A2EB2FEF00A8 /* ObjCAmplitude.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCAmplitude.swift; sourceTree = ""; }; 8EDEC17A6A0F8112A5DC8CEB /* DefaultTrackingOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultTrackingOptions.swift; sourceTree = ""; }; + 8EDEC18449A3E12FCADAD756 /* ObjCProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCProperties.swift; sourceTree = ""; }; 8EDEC19F9FBC98A0D4E5A513 /* RemnantDataMigration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemnantDataMigration.swift; sourceTree = ""; }; 8EDEC2B8B38E04CDB51F0E83 /* Sessions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sessions.swift; sourceTree = ""; }; 8EDEC448C42C8C0A464FAA15 /* BasePlugins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePlugins.swift; sourceTree = ""; }; + 8EDEC44D23EBD92D7892628E /* ObjCDefaultTrackingOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCDefaultTrackingOptions.swift; sourceTree = ""; }; 8EDEC48916EFEF6D5B3EEF9A /* AnalyticsConnectorIdentityPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnalyticsConnectorIdentityPlugin.swift; sourceTree = ""; }; + 8EDEC48D9C242C1A39AABCC4 /* ObjCTrackingOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCTrackingOptions.swift; sourceTree = ""; }; 8EDEC4F83BFAA664749FAEF0 /* QueueTimeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueueTimeTests.swift; sourceTree = ""; }; + 8EDEC500EBDA8B813056E2DB /* ObjCIngestionMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCIngestionMetadata.swift; sourceTree = ""; }; 8EDEC54AB4DF9E1074C3D6A4 /* Weak.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = ""; }; + 8EDEC5A50E197C9C5067C19E /* ObjCIdentify.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCIdentify.swift; sourceTree = ""; }; 8EDEC650EF79B104DC3C9F4C /* UIKitScreenViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKitScreenViews.swift; sourceTree = ""; }; 8EDEC6A9899998F823C278F7 /* RemnantDataMigrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemnantDataMigrationTests.swift; sourceTree = ""; }; + 8EDEC7D924907683D0A14CA6 /* ObjCScreenViewedEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCScreenViewedEvent.swift; sourceTree = ""; }; 8EDEC9B98272069D70D08EA4 /* StoragePrefixMigrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoragePrefixMigrationTests.swift; sourceTree = ""; }; + 8EDECB1FA2AFF022A19104EE /* ObjCPlan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCPlan.swift; sourceTree = ""; }; 8EDECB455E34133AB256D720 /* ScreenViewedEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenViewedEvent.swift; sourceTree = ""; }; + 8EDECB730131FFFBE11EF9FF /* ObjCRevenue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCRevenue.swift; sourceTree = ""; }; 8EDECC2335BCF4C2EC3A6206 /* StoragePrefixMigration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoragePrefixMigration.swift; sourceTree = ""; }; 8EDECC405793463C10F52EF2 /* DeepLinkOpenedEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkOpenedEvent.swift; sourceTree = ""; }; + 8EDECCFD935A0C5A6FE85E87 /* ObjCBaseEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCBaseEvent.swift; sourceTree = ""; }; 8EDECD39BAA97DD4320C0AA5 /* AnalyticsConnectorPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnalyticsConnectorPlugin.swift; sourceTree = ""; }; + 8EDECD4B7ED7CBCD6955D097 /* ObjCLoggerProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCLoggerProvider.swift; sourceTree = ""; }; + 8EDECD6CF59CA94D5D0256CD /* ObjCDeepLinkOpenedEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCDeepLinkOpenedEvent.swift; sourceTree = ""; }; + 8EDECE07F682FAAE47F77B24 /* ObjCEventOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCEventOptions.swift; sourceTree = ""; }; + 8EDECEC5AAE15FD05E76359A /* ObjCConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCConfiguration.swift; sourceTree = ""; }; + 8EDECF8CF745F7339B65D6DB /* ObjCStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCStorage.swift; sourceTree = ""; }; BA0359C92A51585D007C383B /* legacy_v3.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = legacy_v3.sqlite; sourceTree = ""; }; BA0639F52A4DD491000F1CEE /* LegacyDatabaseStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyDatabaseStorage.swift; sourceTree = ""; }; BA1EC0F32A9F2FC700C2D547 /* DefaultTrackingOptionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTrackingOptionsTests.swift; sourceTree = ""; }; @@ -236,6 +268,29 @@ path = Migration; sourceTree = ""; }; + 8EDECAFD8271434E8DC7BA78 /* ObjC */ = { + isa = PBXGroup; + children = ( + 8EDECEC5AAE15FD05E76359A /* ObjCConfiguration.swift */, + 8EDECB1FA2AFF022A19104EE /* ObjCPlan.swift */, + 8EDEC500EBDA8B813056E2DB /* ObjCIngestionMetadata.swift */, + 8EDEC1576C95A2EB2FEF00A8 /* ObjCAmplitude.swift */, + 8EDECE07F682FAAE47F77B24 /* ObjCEventOptions.swift */, + 8EDECCFD935A0C5A6FE85E87 /* ObjCBaseEvent.swift */, + 8EDEC48D9C242C1A39AABCC4 /* ObjCTrackingOptions.swift */, + 8EDEC5A50E197C9C5067C19E /* ObjCIdentify.swift */, + 8EDECB730131FFFBE11EF9FF /* ObjCRevenue.swift */, + 8EDEC1160D95DC3F0E48DDF7 /* ObjCPlugin.swift */, + 8EDEC18449A3E12FCADAD756 /* ObjCProperties.swift */, + 8EDECD4B7ED7CBCD6955D097 /* ObjCLoggerProvider.swift */, + 8EDEC44D23EBD92D7892628E /* ObjCDefaultTrackingOptions.swift */, + 8EDEC7D924907683D0A14CA6 /* ObjCScreenViewedEvent.swift */, + 8EDECD6CF59CA94D5D0256CD /* ObjCDeepLinkOpenedEvent.swift */, + 8EDECF8CF745F7339B65D6DB /* ObjCStorage.swift */, + ); + path = ObjC; + sourceTree = ""; + }; 8EDECBC5925DC68913C7CB89 /* Migration */ = { isa = PBXGroup; children = ( @@ -427,6 +482,7 @@ 8EDEC2B8B38E04CDB51F0E83 /* Sessions.swift */, 8EDEC33A32439724A363C433 /* Migration */, 8EDEC17A6A0F8112A5DC8CEB /* DefaultTrackingOptions.swift */, + 8EDECAFD8271434E8DC7BA78 /* ObjC */, ); name = Sources; path = Sources/Amplitude; @@ -646,6 +702,22 @@ 8EDEC94F3562C2FACAA58A3D /* Weak.swift in Sources */, 8EDEC43FB30802F70112E577 /* ScreenViewedEvent.swift in Sources */, 8EDEC51F746CC25D27E32F6A /* DeepLinkOpenedEvent.swift in Sources */, + 8EDECF81C2B1B38D472FD7EF /* ObjCConfiguration.swift in Sources */, + 8EDECB800546E37719391E65 /* ObjCPlan.swift in Sources */, + 8EDEC02B99EE2092B567A61D /* ObjCIngestionMetadata.swift in Sources */, + 8EDEC98799DCB6610B8DBA19 /* ObjCAmplitude.swift in Sources */, + 8EDECD1E851511887BE000F9 /* ObjCEventOptions.swift in Sources */, + 8EDEC10C56FA7F7DEEB48B6F /* ObjCBaseEvent.swift in Sources */, + 8EDECDF8E188CD7F24EEB475 /* ObjCTrackingOptions.swift in Sources */, + 8EDECEC5F98F9974DF3E576F /* ObjCIdentify.swift in Sources */, + 8EDECBA91C61037025B85EFA /* ObjCRevenue.swift in Sources */, + 8EDECE3AA54253BA167E89CB /* ObjCPlugin.swift in Sources */, + 8EDEC2155081F99515DF9C82 /* ObjCProperties.swift in Sources */, + 8EDEC74C71FEC9056DC7358F /* ObjCLoggerProvider.swift in Sources */, + 8EDECA4DAFA67CD4785D0161 /* ObjCDefaultTrackingOptions.swift in Sources */, + 8EDEC43520B2DCF584F1035D /* ObjCScreenViewedEvent.swift in Sources */, + 8EDECC1FC97DDF0BEFAA96E7 /* ObjCDeepLinkOpenedEvent.swift in Sources */, + 8EDEC5F7208B1C327C8703D7 /* ObjCStorage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Examples/AmplitudeObjCExample/.gitignore b/Examples/AmplitudeObjCExample/.gitignore new file mode 100644 index 00000000..c5451120 --- /dev/null +++ b/Examples/AmplitudeObjCExample/.gitignore @@ -0,0 +1 @@ +xcuserdata/ diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.pbxproj b/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..624a6136 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.pbxproj @@ -0,0 +1,528 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + BA2E1DA42AC1EA220074E74F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BA2E1DA32AC1EA220074E74F /* AppDelegate.m */; }; + BA2E1DA72AC1EA220074E74F /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BA2E1DA62AC1EA220074E74F /* SceneDelegate.m */; }; + BA2E1DAA2AC1EA220074E74F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BA2E1DA92AC1EA220074E74F /* ViewController.m */; }; + BA2E1DAD2AC1EA220074E74F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BA2E1DAB2AC1EA220074E74F /* Main.storyboard */; }; + BA2E1DAF2AC1EA230074E74F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BA2E1DAE2AC1EA230074E74F /* Assets.xcassets */; }; + BA2E1DB22AC1EA230074E74F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BA2E1DB02AC1EA230074E74F /* LaunchScreen.storyboard */; }; + BA2E1DB52AC1EA230074E74F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BA2E1DB42AC1EA230074E74F /* main.m */; }; + BA2E1DBF2AC1EA240074E74F /* AmplitudeObjCExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA2E1DBE2AC1EA240074E74F /* AmplitudeObjCExampleTests.m */; }; + BA2E1DDE2AC1F1CA0074E74F /* AmplitudeSwift in Frameworks */ = {isa = PBXBuildFile; productRef = BA2E1DDD2AC1F1CA0074E74F /* AmplitudeSwift */; }; + BAB79A442AC3042200F191C9 /* AmplitudeSwift in Frameworks */ = {isa = PBXBuildFile; productRef = BAB79A432AC3042200F191C9 /* AmplitudeSwift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + BA2E1DBB2AC1EA230074E74F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2E1D972AC1EA220074E74F /* Project object */; + proxyType = 1; + remoteGlobalIDString = BA2E1D9E2AC1EA220074E74F; + remoteInfo = AmplitudeObjCExample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + BA2E1D9F2AC1EA220074E74F /* AmplitudeObjCExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AmplitudeObjCExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BA2E1DA22AC1EA220074E74F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + BA2E1DA32AC1EA220074E74F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + BA2E1DA52AC1EA220074E74F /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + BA2E1DA62AC1EA220074E74F /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; + BA2E1DA82AC1EA220074E74F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + BA2E1DA92AC1EA220074E74F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + BA2E1DAC2AC1EA220074E74F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + BA2E1DAE2AC1EA230074E74F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BA2E1DB12AC1EA230074E74F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + BA2E1DB32AC1EA230074E74F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BA2E1DB42AC1EA230074E74F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + BA2E1DBA2AC1EA230074E74F /* AmplitudeObjCExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AmplitudeObjCExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BA2E1DBE2AC1EA240074E74F /* AmplitudeObjCExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AmplitudeObjCExampleTests.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BA2E1D9C2AC1EA220074E74F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BA2E1DDE2AC1F1CA0074E74F /* AmplitudeSwift in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BA2E1DB72AC1EA230074E74F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BAB79A442AC3042200F191C9 /* AmplitudeSwift in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BA2E1D962AC1EA220074E74F = { + isa = PBXGroup; + children = ( + BA2E1DA12AC1EA220074E74F /* AmplitudeObjCExample */, + BA2E1DBD2AC1EA240074E74F /* AmplitudeObjCExampleTests */, + BA2E1DA02AC1EA220074E74F /* Products */, + BAB79A422AC3042200F191C9 /* Frameworks */, + ); + sourceTree = ""; + }; + BA2E1DA02AC1EA220074E74F /* Products */ = { + isa = PBXGroup; + children = ( + BA2E1D9F2AC1EA220074E74F /* AmplitudeObjCExample.app */, + BA2E1DBA2AC1EA230074E74F /* AmplitudeObjCExampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BA2E1DA12AC1EA220074E74F /* AmplitudeObjCExample */ = { + isa = PBXGroup; + children = ( + BA2E1DA22AC1EA220074E74F /* AppDelegate.h */, + BA2E1DA32AC1EA220074E74F /* AppDelegate.m */, + BA2E1DA52AC1EA220074E74F /* SceneDelegate.h */, + BA2E1DA62AC1EA220074E74F /* SceneDelegate.m */, + BA2E1DA82AC1EA220074E74F /* ViewController.h */, + BA2E1DA92AC1EA220074E74F /* ViewController.m */, + BA2E1DAB2AC1EA220074E74F /* Main.storyboard */, + BA2E1DAE2AC1EA230074E74F /* Assets.xcassets */, + BA2E1DB02AC1EA230074E74F /* LaunchScreen.storyboard */, + BA2E1DB32AC1EA230074E74F /* Info.plist */, + BA2E1DB42AC1EA230074E74F /* main.m */, + ); + path = AmplitudeObjCExample; + sourceTree = ""; + }; + BA2E1DBD2AC1EA240074E74F /* AmplitudeObjCExampleTests */ = { + isa = PBXGroup; + children = ( + BA2E1DBE2AC1EA240074E74F /* AmplitudeObjCExampleTests.m */, + ); + path = AmplitudeObjCExampleTests; + sourceTree = ""; + }; + BAB79A422AC3042200F191C9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BA2E1D9E2AC1EA220074E74F /* AmplitudeObjCExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = BA2E1DCE2AC1EA240074E74F /* Build configuration list for PBXNativeTarget "AmplitudeObjCExample" */; + buildPhases = ( + BA2E1D9B2AC1EA220074E74F /* Sources */, + BA2E1D9C2AC1EA220074E74F /* Frameworks */, + BA2E1D9D2AC1EA220074E74F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AmplitudeObjCExample; + packageProductDependencies = ( + BA2E1DDD2AC1F1CA0074E74F /* AmplitudeSwift */, + ); + productName = AmplitudeObjCExample; + productReference = BA2E1D9F2AC1EA220074E74F /* AmplitudeObjCExample.app */; + productType = "com.apple.product-type.application"; + }; + BA2E1DB92AC1EA230074E74F /* AmplitudeObjCExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BA2E1DD12AC1EA240074E74F /* Build configuration list for PBXNativeTarget "AmplitudeObjCExampleTests" */; + buildPhases = ( + BA2E1DB62AC1EA230074E74F /* Sources */, + BA2E1DB72AC1EA230074E74F /* Frameworks */, + BA2E1DB82AC1EA230074E74F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BA2E1DBC2AC1EA230074E74F /* PBXTargetDependency */, + ); + name = AmplitudeObjCExampleTests; + packageProductDependencies = ( + BAB79A432AC3042200F191C9 /* AmplitudeSwift */, + ); + productName = AmplitudeObjCExampleTests; + productReference = BA2E1DBA2AC1EA230074E74F /* AmplitudeObjCExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BA2E1D972AC1EA220074E74F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1420; + TargetAttributes = { + BA2E1D9E2AC1EA220074E74F = { + CreatedOnToolsVersion = 14.2; + }; + BA2E1DB92AC1EA230074E74F = { + CreatedOnToolsVersion = 14.2; + TestTargetID = BA2E1D9E2AC1EA220074E74F; + }; + }; + }; + buildConfigurationList = BA2E1D9A2AC1EA220074E74F /* Build configuration list for PBXProject "AmplitudeObjCExample" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BA2E1D962AC1EA220074E74F; + packageReferences = ( + BA2E1DDC2AC1F1CA0074E74F /* XCRemoteSwiftPackageReference "Amplitude-Swift" */, + ); + productRefGroup = BA2E1DA02AC1EA220074E74F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BA2E1D9E2AC1EA220074E74F /* AmplitudeObjCExample */, + BA2E1DB92AC1EA230074E74F /* AmplitudeObjCExampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BA2E1D9D2AC1EA220074E74F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BA2E1DB22AC1EA230074E74F /* LaunchScreen.storyboard in Resources */, + BA2E1DAF2AC1EA230074E74F /* Assets.xcassets in Resources */, + BA2E1DAD2AC1EA220074E74F /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BA2E1DB82AC1EA230074E74F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BA2E1D9B2AC1EA220074E74F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BA2E1DAA2AC1EA220074E74F /* ViewController.m in Sources */, + BA2E1DA42AC1EA220074E74F /* AppDelegate.m in Sources */, + BA2E1DB52AC1EA230074E74F /* main.m in Sources */, + BA2E1DA72AC1EA220074E74F /* SceneDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BA2E1DB62AC1EA230074E74F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BA2E1DBF2AC1EA240074E74F /* AmplitudeObjCExampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BA2E1DBC2AC1EA230074E74F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BA2E1D9E2AC1EA220074E74F /* AmplitudeObjCExample */; + targetProxy = BA2E1DBB2AC1EA230074E74F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + BA2E1DAB2AC1EA220074E74F /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BA2E1DAC2AC1EA220074E74F /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + BA2E1DB02AC1EA230074E74F /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BA2E1DB12AC1EA230074E74F /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + BA2E1DCC2AC1EA240074E74F /* 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++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + BA2E1DCD2AC1EA240074E74F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + BA2E1DCF2AC1EA240074E74F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AmplitudeObjCExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.amplitude.AmplitudeObjCExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BA2E1DD02AC1EA240074E74F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AmplitudeObjCExample/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.amplitude.AmplitudeObjCExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BA2E1DD22AC1EA240074E74F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.amplitude.AmplitudeObjCExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AmplitudeObjCExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AmplitudeObjCExample"; + }; + name = Debug; + }; + BA2E1DD32AC1EA240074E74F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.amplitude.AmplitudeObjCExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AmplitudeObjCExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AmplitudeObjCExample"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BA2E1D9A2AC1EA220074E74F /* Build configuration list for PBXProject "AmplitudeObjCExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BA2E1DCC2AC1EA240074E74F /* Debug */, + BA2E1DCD2AC1EA240074E74F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BA2E1DCE2AC1EA240074E74F /* Build configuration list for PBXNativeTarget "AmplitudeObjCExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BA2E1DCF2AC1EA240074E74F /* Debug */, + BA2E1DD02AC1EA240074E74F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BA2E1DD12AC1EA240074E74F /* Build configuration list for PBXNativeTarget "AmplitudeObjCExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BA2E1DD22AC1EA240074E74F /* Debug */, + BA2E1DD32AC1EA240074E74F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + BA2E1DDC2AC1F1CA0074E74F /* XCRemoteSwiftPackageReference "Amplitude-Swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "../../../Amplitude-Swift"; + requirement = { + branch = HEAD; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + BA2E1DDD2AC1F1CA0074E74F /* AmplitudeSwift */ = { + isa = XCSwiftPackageProductDependency; + package = BA2E1DDC2AC1F1CA0074E74F /* XCRemoteSwiftPackageReference "Amplitude-Swift" */; + productName = AmplitudeSwift; + }; + BAB79A432AC3042200F191C9 /* AmplitudeSwift */ = { + isa = XCSwiftPackageProductDependency; + package = BA2E1DDC2AC1F1CA0074E74F /* XCRemoteSwiftPackageReference "Amplitude-Swift" */; + productName = AmplitudeSwift; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = BA2E1D972AC1EA220074E74F /* Project object */; +} diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/AppDelegate.h b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/AppDelegate.h new file mode 100644 index 00000000..d79858b9 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/AppDelegate.h @@ -0,0 +1,7 @@ +#import + +@interface AppDelegate : UIResponder + + +@end + diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/AppDelegate.m b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/AppDelegate.m new file mode 100644 index 00000000..723af350 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/AppDelegate.m @@ -0,0 +1,85 @@ +#import "AppDelegate.h" +@import AmplitudeSwift; + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + NSString* apiKey = @"API-KEY"; + AMPConfiguration* configuration = [AMPConfiguration initWithApiKey:apiKey]; + configuration.logLevel = AMPLogLevelLOG; + configuration.serverZone = AMPServerZoneUS; + configuration.defaultTracking = AMPDefaultTrackingOptions.ALL; + configuration.loggerProvider = ^(NSInteger logLevel, NSString* _Nonnull message) { + NSLog(@"%@", message); + }; + Amplitude* amplitude = [Amplitude initWithConfiguration:configuration]; + + [amplitude add:[AMPPlugin initWithType:AMPPluginTypeBefore execute:^AMPBaseEvent* _Nullable(AMPBaseEvent* _Nonnull event) { + [event.eventProperties set:@"plugin-prop" value:@234]; + event.locationLat = 34; + event.locationLng = 78; + return event; + }]]; + + [amplitude setUserId:@"User-ObjC"]; + + AMPIdentify* identify = [AMPIdentify new]; + [identify set:@"user-prop-1" value:@"value-1"]; + [identify set:@"user-prop-2" value:@123]; + [amplitude identify:identify]; + + [amplitude setGroup:@"orgName" groupName:@"Test Org"]; + + AMPIdentify* groupIdentify = [AMPIdentify new]; + [groupIdentify set:@"group-prop-1" value:@"value-A"]; + [groupIdentify set:@"group-prop-2" value:@true]; + [amplitude groupIdentify:@"orgName" groupName:@"Test Org" identify:groupIdentify]; + + [amplitude track:@"Event-A" eventProperties:@{ + @"prop-string": @"value-A", + @"prop-int": @111, + @"prop-string-array": @[@"item-1", @"item-2"] + }]; + + AMPBaseEvent* event = [AMPBaseEvent initWithEventType:@"Event-B"]; + event.appVersion = @"1.2.3"; + + [amplitude track:event callback:^(AMPBaseEvent* _Nonnull event, NSInteger code, NSString* _Nonnull message) { + NSLog(@"%@ - %@", event.eventType, message); + }]; + + AMPBaseEvent* deepLinkOpenedEvent = [AMPDeepLinkOpenedEvent initWithUrl:@"http://example.com" referrer:@"https://referrer.com"]; + [amplitude track:deepLinkOpenedEvent]; + + AMPBaseEvent* screenViewedEvent = [AMPScreenViewedEvent initWithScreenName:@"Settings"]; + [amplitude track:screenViewedEvent]; + + [amplitude flush]; + + return YES; +} + + +#pragma mark - UISceneSession lifecycle + + +- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; +} + + +- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. +} + + +@end diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..13613e3e --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/Contents.json b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Base.lproj/LaunchScreen.storyboard b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Base.lproj/Main.storyboard b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..baf106e3 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Base.lproj/Main.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Info.plist b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Info.plist new file mode 100644 index 00000000..81ed29b7 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/Info.plist @@ -0,0 +1,25 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + UISceneStoryboardFile + Main + + + + + + diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/SceneDelegate.h b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/SceneDelegate.h new file mode 100644 index 00000000..a19a1ce5 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/SceneDelegate.h @@ -0,0 +1,8 @@ +#import + +@interface SceneDelegate : UIResponder + +@property (strong, nonatomic) UIWindow * window; + +@end + diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/SceneDelegate.m b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/SceneDelegate.m new file mode 100644 index 00000000..e5ae2c95 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/SceneDelegate.m @@ -0,0 +1,50 @@ +#import "SceneDelegate.h" + +@interface SceneDelegate () + +@end + +@implementation SceneDelegate + + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). +} + + +- (void)sceneDidDisconnect:(UIScene *)scene { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). +} + + +- (void)sceneDidBecomeActive:(UIScene *)scene { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. +} + + +- (void)sceneWillResignActive:(UIScene *)scene { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). +} + + +- (void)sceneWillEnterForeground:(UIScene *)scene { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. +} + + +- (void)sceneDidEnterBackground:(UIScene *)scene { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. +} + + +@end diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/ViewController.h b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/ViewController.h new file mode 100644 index 00000000..a79652b5 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/ViewController.h @@ -0,0 +1,7 @@ +#import + +@interface ViewController : UIViewController + + +@end + diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/ViewController.m b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/ViewController.m new file mode 100644 index 00000000..1c05fe04 --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/ViewController.m @@ -0,0 +1,15 @@ +#import "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.title = @"Main View"; +} + + +@end diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExample/main.m b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/main.m new file mode 100644 index 00000000..dba295eb --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExample/main.m @@ -0,0 +1,11 @@ +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + NSString * appDelegateClassName; + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + appDelegateClassName = NSStringFromClass([AppDelegate class]); + } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +} diff --git a/Examples/AmplitudeObjCExample/AmplitudeObjCExampleTests/AmplitudeObjCExampleTests.m b/Examples/AmplitudeObjCExample/AmplitudeObjCExampleTests/AmplitudeObjCExampleTests.m new file mode 100644 index 00000000..2e90b4ff --- /dev/null +++ b/Examples/AmplitudeObjCExample/AmplitudeObjCExampleTests/AmplitudeObjCExampleTests.m @@ -0,0 +1,235 @@ +#import +@import AmplitudeSwift; + +@interface AmplitudeObjCExampleTests : XCTestCase + +@end + +@implementation AmplitudeObjCExampleTests + +- (void)testTrack { + Amplitude* amplitude = [self getAmplitude:@"track"]; + + NSDictionary* eventProperties = @{ + @"prop-string": @"string-value", + @"prop-int": @111, + @"prop-number": @12.3, + @"prop-boolean": @true, + @"prop-string-array": @[@"item-1", @"item-2"], + @"prop-int-array": @[@1, @2, @3], + @"prop-number-array": @[@1.1, @2.2, @3.3], + @"prop-bool-array": @[@true, @false, @true], + @"prop-object": @{@"nested-prop-1": @555, @"nested-prop-2": @"nested-string"} + }; + [amplitude track:@"Event-A" eventProperties:eventProperties]; + + NSArray* eventsStrings = [amplitude.storage getEventsStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 1); + XCTAssertEqualObjects(@"Event-A", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(eventProperties, [events[0] objectForKey:@"event_properties"]); +} + +- (void)testTrack_Options { + Amplitude* amplitude = [self getAmplitude:@"track_options"]; + + AMPEventOptions* eventOptions = [AMPEventOptions new]; + eventOptions.locationLat = 12; + eventOptions.locationLng = 34; + NSDictionary* eventProperties = @{ + @"prop-string": @"string-value", + @"prop-int": @111 + }; + [amplitude track:@"Event-A" eventProperties:eventProperties options:eventOptions]; + + NSArray* eventsStrings = [amplitude.storage getEventsStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 1); + XCTAssertEqualObjects(@"Event-A", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(eventProperties, [events[0] objectForKey:@"event_properties"]); + XCTAssertEqualObjects(@12, [events[0] objectForKey:@"location_lat"]); + XCTAssertEqualObjects(@34, [events[0] objectForKey:@"location_lng"]); +} + +- (void)testIdentify { + Amplitude* amplitude = [self getAmplitude:@"identify"]; + + AMPIdentify* identify = [AMPIdentify new]; + [identify set:@"user-string-prop" value:@"string-value"]; + [identify setOnce:@"user-int-prop" value:@111]; + [identify append:@"user-number-prop" value:@123.4]; + [identify prepend:@"user-bool-prop" value:@true]; + [identify add:@"user-sum-prop" valueInt:7]; + [identify remove:@"user-agg-prop" value: @"item1"]; + [identify unset:@"user-deprecated-prop"]; + [amplitude identify:identify]; + + NSDictionary* expectedUserProperties = @{ + @"$set": @{@"user-string-prop": @"string-value"}, + @"$set_once": @{@"user-int-prop": @111}, + @"$append": @{@"user-number-prop": @123.4}, + @"$prepend": @{@"user-bool-prop": @true}, + @"$add": @{@"user-sum-prop": @7}, + @"$remove": @{@"user-agg-prop": @"item1"}, + @"$unset": @{@"user-deprecated-prop": @"-"} + }; + + NSArray* eventsStrings = [amplitude.storage getEventsStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 1); + XCTAssertEqualObjects(@"$identify", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(expectedUserProperties, [events[0] objectForKey:@"user_properties"]); +} + +- (void)testIdentify_ClearAll { + Amplitude* amplitude = [self getAmplitude:@"identify-clearAll"]; + + AMPIdentify* identify = [AMPIdentify new]; + [identify clearAll]; + [amplitude identify:identify]; + + NSDictionary* expectedUserProperties = @{ + @"$clearAll": @"-" + }; + + NSArray* eventsStrings = [amplitude.storage getEventsStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 1); + XCTAssertEqualObjects(@"$identify", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(expectedUserProperties, [events[0] objectForKey:@"user_properties"]); +} + +- (void)testIdentify_InterceptedIdentifies { + Amplitude* amplitude = [self getAmplitude:@"identify-interceptedIdentifies"]; + + AMPIdentify* identify1 = [AMPIdentify new]; + [identify1 set:@"user-string-prop" value:@"string-value"]; + [amplitude identify:identify1]; + + AMPIdentify* identify2 = [AMPIdentify new]; + [identify2 set:@"user-int-prop" value:@111]; + [amplitude identify:identify2]; + + NSDictionary* expectedUserProperties1 = @{ + @"$set": @{@"user-string-prop": @"string-value"} + }; + + NSDictionary* expectedUserProperties2 = @{ + @"$set": @{@"user-int-prop": @111} + }; + + NSArray* eventsStrings = [amplitude.storage getInterceptedIdentifiesStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 2); + XCTAssertEqualObjects(@"$identify", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(expectedUserProperties1, [events[0] objectForKey:@"user_properties"]); + XCTAssertEqualObjects(@"$identify", [events[1] objectForKey:@"event_type"]); + XCTAssertEqualObjects(expectedUserProperties2, [events[1] objectForKey:@"user_properties"]); +} + +- (void)testGroupIdentify { + Amplitude* amplitude = [self getAmplitude:@"groupIdentify"]; + + AMPIdentify* identify = [AMPIdentify new]; + [identify set:@"user-string-prop" value:@"string-value"]; + [identify setOnce:@"user-int-prop" value:@111]; + [identify append:@"user-number-prop" value:@123.4]; + [identify prepend:@"user-bool-prop" value:@true]; + [identify add:@"user-sum-prop" valueInt:7]; + [identify remove:@"user-agg-prop" value: @"item1"]; + [identify unset:@"user-deprecated-prop"]; + [amplitude groupIdentify:@"type-1" groupName:@"name-1" identify:identify]; + + NSDictionary* expectedGroupProperties = @{ + @"$set": @{@"user-string-prop": @"string-value"}, + @"$set_once": @{@"user-int-prop": @111}, + @"$append": @{@"user-number-prop": @123.4}, + @"$prepend": @{@"user-bool-prop": @true}, + @"$add": @{@"user-sum-prop": @7}, + @"$remove": @{@"user-agg-prop": @"item1"}, + @"$unset": @{@"user-deprecated-prop": @"-"} + }; + + NSArray* eventsStrings = [amplitude.storage getEventsStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 1); + XCTAssertEqualObjects(@"$groupidentify", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(@{@"type-1": @"name-1"}, [events[0] objectForKey:@"groups"]); + XCTAssertEqualObjects(expectedGroupProperties, [events[0] objectForKey:@"group_properties"]); +} + +- (void)testSetGroup { + Amplitude* amplitude = [self getAmplitude:@"setGroup"]; + + NSString* groupName = @"name-1"; + [amplitude setGroup:@"type-1" groupName:groupName]; + + NSArray* eventsStrings = [amplitude.storage getEventsStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 1); + XCTAssertEqualObjects(@"$identify", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(@{@"type-1": groupName}, [events[0] objectForKey:@"groups"]); + XCTAssertEqualObjects(@{@"$set": @{@"type-1": groupName}}, [events[0] objectForKey:@"user_properties"]); +} + +- (void)testSetGroup_Multiple { + Amplitude* amplitude = [self getAmplitude:@"setGroup_Multiple"]; + + NSArray* groupNames = @[@"name-1", @"name-2"]; + [amplitude setGroup:@"type-1" groupNames:groupNames]; + + NSArray* eventsStrings = [amplitude.storage getEventsStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 1); + XCTAssertEqualObjects(@"$identify", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(@{@"type-1": groupNames}, [events[0] objectForKey:@"groups"]); + XCTAssertEqualObjects(@{@"$set": @{@"type-1": groupNames}}, [events[0] objectForKey:@"user_properties"]); +} + +- (void)testPlugin { + Amplitude* amplitude = [self getAmplitude:@"plugin"]; + [amplitude add:[AMPPlugin initWithType:AMPPluginTypeBefore execute:^AMPBaseEvent* _Nullable(AMPBaseEvent* _Nonnull event) { + [event.eventProperties set:@"plugin-prop" value:@"plugin-value"]; + event.locationLat = 12; + event.locationLng = 34; + return event; + }]]; + + [amplitude track:@"Event-A" eventProperties:@{ + @"prop-string": @"string-value", + @"prop-int": @111 + }]; + + NSDictionary* expectedEventProperties = @{ + @"prop-string": @"string-value", + @"prop-int": @111, + @"plugin-prop": @"plugin-value" + }; + + NSArray* eventsStrings = [amplitude.storage getEventsStrings]; + NSArray* events = [self parseEvents:eventsStrings]; + XCTAssertEqual(events.count, 1); + XCTAssertEqualObjects(@"Event-A", [events[0] objectForKey:@"event_type"]); + XCTAssertEqualObjects(expectedEventProperties, [events[0] objectForKey:@"event_properties"]); + XCTAssertEqualObjects(@12, [events[0] objectForKey:@"location_lat"]); + XCTAssertEqualObjects(@34, [events[0] objectForKey:@"location_lng"]); +} + +- (Amplitude *)getAmplitude:(NSString *)instancePrefix { + NSString* instanceName = [NSString stringWithFormat:@"%@-%f", instancePrefix, [[NSDate date] timeIntervalSince1970]]; + AMPConfiguration* configuration = [AMPConfiguration initWithApiKey:@"API-KEY" instanceName:instanceName]; + configuration.defaultTracking = AMPDefaultTrackingOptions.NONE; + Amplitude* amplitude = [Amplitude initWithConfiguration:configuration]; + return amplitude; +} + +- (NSArray *)parseEvents:(NSArray*)eventsStrings { + XCTAssertEqual(1, eventsStrings.count); + NSError* jsonError = nil; + NSData* data = [eventsStrings[0] dataUsingEncoding:NSUTF8StringEncoding]; + NSArray* events = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError]; + XCTAssertNil(jsonError); + return events; +} + +@end diff --git a/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/AmplitudeSwiftUIExampleApp.swift b/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/AmplitudeSwiftUIExampleApp.swift index 3f4c4529..9e8f6202 100644 --- a/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/AmplitudeSwiftUIExampleApp.swift +++ b/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/AmplitudeSwiftUIExampleApp.swift @@ -76,7 +76,7 @@ extension Amplitude { callback: { (event: BaseEvent, code: Int, message: String) -> Void in print("eventcallback: \(event), code: \(code), message: \(message)") }, - trackingOptions: TrackingOptions().disableCarrier().disableTrackDMA(), + trackingOptions: TrackingOptions().disableTrackCarrier().disableTrackDMA(), flushEventsOnClose: true, minTimeBetweenSessionsMillis: 15000 ) diff --git a/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/ExamplePlugins/TroubleShootingPlugin.swift b/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/ExamplePlugins/TroubleShootingPlugin.swift index 3befb6de..30b4a9e6 100644 --- a/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/ExamplePlugins/TroubleShootingPlugin.swift +++ b/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/ExamplePlugins/TroubleShootingPlugin.swift @@ -15,7 +15,7 @@ class TroubleShootingPlugin: DestinationPlugin { let serverZone = amplitude.configuration.serverZone.rawValue; let serverUrl = amplitude.configuration.serverUrl ?? "null"; - self.amplitude?.logger?.debug(message: "Current Configuration : {\"apiKey\": "+apiKey+", \"serverZone\": "+serverZone+", \"serverUrl\": "+serverUrl+"}") + self.amplitude?.logger?.debug(message: "Current Configuration : {\"apiKey\": "+apiKey+", \"serverZone\": "+serverZone.rawValue+", \"serverUrl\": "+serverUrl+"}") } open override func track(event: BaseEvent) -> BaseEvent? { diff --git a/Sources/Amplitude/Configuration.swift b/Sources/Amplitude/Configuration.swift index 12ff69ae..4046c3d7 100644 --- a/Sources/Amplitude/Configuration.swift +++ b/Sources/Amplitude/Configuration.swift @@ -11,7 +11,7 @@ public class Configuration { public internal(set) var apiKey: String public var flushQueueSize: Int public var flushIntervalMillis: Int - public let instanceName: String + public internal(set) var instanceName: String public var optOut: Bool public let storageProvider: any Storage public let identifyStorageProvider: any Storage @@ -26,12 +26,12 @@ public class Configuration { public var serverUrl: String? public var plan: Plan? public var ingestionMetadata: IngestionMetadata? - public var trackingOptions: TrackingOptions? - public var enableCoppaControl: Bool? - public var flushEventsOnClose: Bool? + public var trackingOptions: TrackingOptions + public var enableCoppaControl: Bool + public var flushEventsOnClose: Bool public var minTimeBetweenSessionsMillis: Int public var identifyBatchIntervalMillis: Int - public let migrateLegacyData: Bool + public internal(set) var migrateLegacyData: Bool public var defaultTracking: DefaultTrackingOptions public init( diff --git a/Sources/Amplitude/Constants.swift b/Sources/Amplitude/Constants.swift index 90943fac..0afe9b92 100644 --- a/Sources/Amplitude/Constants.swift +++ b/Sources/Amplitude/Constants.swift @@ -7,6 +7,7 @@ import Foundation +@objc(AMPLogLevel) public enum LogLevelEnum: Int { case OFF case ERROR @@ -15,9 +16,32 @@ public enum LogLevelEnum: Int { case DEBUG } -public enum ServerZone: String { +@objc(AMPServerZone) +public enum ServerZone: Int { case US case EU + + public typealias RawValue = String + + public var rawValue: RawValue { + switch self { + case .US: + return "US" + case .EU: + return "EU" + } + } + + public init?(rawValue: RawValue) { + switch rawValue { + case "US": + self = .US + case "EU": + self = .EU + default: + return nil + } + } } public struct Constants { diff --git a/Sources/Amplitude/Events/Identify.swift b/Sources/Amplitude/Events/Identify.swift index aef42ae1..997ade7b 100644 --- a/Sources/Amplitude/Events/Identify.swift +++ b/Sources/Amplitude/Events/Identify.swift @@ -157,6 +157,12 @@ public class Identify { return self } + @discardableResult + public func setOnce(property: String, value: Any?) -> Identify { + setUserProperty(operation: .SET_ONCE, property: property, value: value) + return self + } + @discardableResult public func setOnce(property: String, value: [String: Any]) -> Identify { setUserProperty(operation: .SET_ONCE, property: property, value: value) @@ -242,6 +248,12 @@ public class Identify { return self } + @discardableResult + public func prepend(property: String, value: Any?) -> Identify { + setUserProperty(operation: .PREPEND, property: property, value: value) + return self + } + @discardableResult public func prepend(property: String, value: [String: Any]) -> Identify { setUserProperty(operation: .PREPEND, property: property, value: value) @@ -327,6 +339,12 @@ public class Identify { return self } + @discardableResult + public func append(property: String, value: Any?) -> Identify { + setUserProperty(operation: .APPEND, property: property, value: value) + return self + } + @discardableResult public func append(property: String, value: [String: Any]) -> Identify { setUserProperty(operation: .APPEND, property: property, value: value) @@ -412,6 +430,12 @@ public class Identify { return self } + @discardableResult + public func postInsert(property: String, value: Any?) -> Identify { + setUserProperty(operation: .POST_INSERT, property: property, value: value) + return self + } + @discardableResult public func postInsert(property: String, value: [String: Any]) -> Identify { setUserProperty(operation: .POST_INSERT, property: property, value: value) @@ -497,6 +521,12 @@ public class Identify { return self } + @discardableResult + public func preInsert(property: String, value: Any?) -> Identify { + setUserProperty(operation: .PRE_INSERT, property: property, value: value) + return self + } + @discardableResult public func preInsert(property: String, value: [String: Any]) -> Identify { setUserProperty(operation: .PRE_INSERT, property: property, value: value) @@ -582,6 +612,12 @@ public class Identify { return self } + @discardableResult + public func remove(property: String, value: Any?) -> Identify { + setUserProperty(operation: .REMOVE, property: property, value: value) + return self + } + @discardableResult public func remove(property: String, value: [String: Any]) -> Identify { setUserProperty(operation: .REMOVE, property: property, value: value) @@ -657,8 +693,8 @@ public class Identify { // $unset operation @discardableResult - public func unset(proprety: String) -> Identify { - setUserProperty(operation: .UNSET, property: proprety, value: Identify.UNSET_VALUE) + public func unset(property: String) -> Identify { + setUserProperty(operation: .UNSET, property: property, value: Identify.UNSET_VALUE) return self } diff --git a/Sources/Amplitude/ObjC/ObjCAmplitude.swift b/Sources/Amplitude/ObjC/ObjCAmplitude.swift new file mode 100644 index 00000000..eb47b46b --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCAmplitude.swift @@ -0,0 +1,209 @@ +import Foundation + +@objc(Amplitude) +public class ObjCAmplitude: NSObject { + private let amplitude: Amplitude + private var plugins: [ObjCPluginWrapper] = [] + + @objc(initWithConfiguration:) + public static func initWithConfiguration( + configuration: ObjCConfiguration + ) -> ObjCAmplitude { + ObjCAmplitude(configuration: configuration) + } + + @objc(initWithConfiguration:) + public init( + configuration: ObjCConfiguration + ) { + amplitude = Amplitude(configuration: configuration.configuration) + } + + @objc + public var configuration: ObjCConfiguration { + ObjCConfiguration(configuration: amplitude.configuration) + } + + @objc + public var storage: ObjCStorage { + ObjCStorage(amplitude: amplitude) + } + + @objc(track:) + @discardableResult + public func track(event: ObjCBaseEvent) -> ObjCAmplitude { + amplitude.track(event: event.event) + return self + } + + @objc(track:callback:) + @discardableResult + public func track(event: ObjCBaseEvent, callback: ObjCEventCallback?) -> ObjCAmplitude { + amplitude.track(event: event.event, callback: callback == nil ? nil : { (event, code, message) in + callback!(ObjCBaseEvent(event: event), code, message) + }) + return self + } + + @objc(track:options:) + @discardableResult + public func track(event: ObjCBaseEvent, options: ObjCEventOptions?) -> ObjCAmplitude { + amplitude.track(event: event.event, options: options?.options) + return self + } + + @objc(track:options:callback:) + @discardableResult + public func track(event: ObjCBaseEvent, options: ObjCEventOptions?, callback: ObjCEventCallback?) -> ObjCAmplitude { + amplitude.track(event: event.event, options: options?.options, callback: callback == nil ? nil : { (event, code, message) in + callback!(ObjCBaseEvent(event: event), code, message) + }) + return self + } + + @objc(track:eventProperties:) + @discardableResult + public func track(eventType: String, eventProperties: [String: Any]?) -> ObjCAmplitude { + amplitude.track(eventType: eventType, eventProperties: eventProperties) + return self + } + + @objc(track:eventProperties:options:) + @discardableResult + public func track(eventType: String, eventProperties: [String: Any]?, options: ObjCEventOptions?) -> ObjCAmplitude { + amplitude.track(eventType: eventType, eventProperties: eventProperties, options: options?.options) + return self + } + + @objc(identify:) + @discardableResult + public func identify(identify: ObjCIdentify) -> ObjCAmplitude { + amplitude.identify(identify: identify.identify) + return self + } + + @objc(identify:options:) + @discardableResult + public func identify(identify: ObjCIdentify, options: ObjCEventOptions?) -> ObjCAmplitude { + amplitude.identify(identify: identify.identify, options: options?.options) + return self + } + + @objc(groupIdentify:groupName:identify:) + @discardableResult + public func groupIdentify(groupType: String, groupName: String, identify: ObjCIdentify) -> ObjCAmplitude { + amplitude.groupIdentify(groupType: groupType, groupName: groupName, identify: identify.identify) + return self + } + + @objc(groupIdentify:groupName:identify:options:) + @discardableResult + public func groupIdentify(groupType: String, groupName: String, identify: ObjCIdentify, options: ObjCEventOptions?) -> ObjCAmplitude { + amplitude.groupIdentify(groupType: groupType, groupName: groupName, identify: identify.identify, options: options?.options) + return self + } + + @objc(setGroup:groupName:) + @discardableResult + public func setGroup(groupType: String, groupName: String) -> ObjCAmplitude { + amplitude.setGroup(groupType: groupType, groupName: groupName) + return self + } + + @objc(setGroup:groupName:options:) + @discardableResult + public func setGroup(groupType: String, groupName: String, options: ObjCEventOptions?) -> ObjCAmplitude { + amplitude.setGroup(groupType: groupType, groupName: groupName, options: options?.options) + return self + } + + @objc(setGroup:groupNames:) + @discardableResult + public func setGroup(groupType: String, groupNames: [String]) -> ObjCAmplitude { + amplitude.setGroup(groupType: groupType, groupName: groupNames) + return self + } + + @objc(setGroup:groupNames:options:) + @discardableResult + public func setGroup(groupType: String, groupNames: [String], options: ObjCEventOptions?) -> ObjCAmplitude { + amplitude.setGroup(groupType: groupType, groupName: groupNames, options: options?.options) + return self + } + + @objc(revenue:) + @discardableResult + public func revenue(revenue: ObjCRevenue) -> ObjCAmplitude { + amplitude.revenue(revenue: revenue.instance) + return self + } + + @objc(revenue:options:) + @discardableResult + public func revenue(revenue: ObjCRevenue, options: ObjCEventOptions? = nil) -> ObjCAmplitude { + amplitude.revenue(revenue: revenue.instance, options: options?.options) + return self + } + + @objc(add:) + @discardableResult + public func add(plugin: ObjCPlugin) -> ObjCAmplitude { + let wrapper = ObjCPluginWrapper(amplitude: self, wrapped: plugin) + plugins.append(wrapper) + amplitude.add(plugin: wrapper) + return self + } + + @objc(remove:) + @discardableResult + public func remove(plugin: ObjCPlugin) -> ObjCAmplitude { + guard let pluginIndex = plugins.firstIndex(where: { wrapper in wrapper.wrapped == plugin }) else { return self } + let wrapper = plugins[pluginIndex] + plugins.remove(at: pluginIndex) + amplitude.remove(plugin: wrapper) + return self + } + + @objc + @discardableResult + public func flush() -> ObjCAmplitude { + amplitude.flush() + return self + } + + @objc(setUserId:) + @discardableResult + public func setUserId(userId: String?) -> ObjCAmplitude { + amplitude.setUserId(userId: userId) + return self + } + + @objc(setDeviceId:) + @discardableResult + public func setDeviceId(deviceId: String?) -> ObjCAmplitude { + amplitude.setDeviceId(deviceId: deviceId) + return self + } + + @objc + public func getUserId() -> String? { + amplitude.getUserId() + } + + @objc + public func getDeviceId() -> String? { + amplitude.getDeviceId() + } + + @objc + public func getSessionId() -> Int64 { + amplitude.getSessionId() + } + + @objc + @discardableResult + public func reset() -> ObjCAmplitude { + amplitude.reset() + return self + } +} diff --git a/Sources/Amplitude/ObjC/ObjCBaseEvent.swift b/Sources/Amplitude/ObjC/ObjCBaseEvent.swift new file mode 100644 index 00000000..9bf5cb41 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCBaseEvent.swift @@ -0,0 +1,87 @@ +import Foundation + +@objc(AMPBaseEvent) +public class ObjCBaseEvent: ObjCEventOptions { + internal let event: BaseEvent + + internal override var options: EventOptions { + event + } + + @objc(initWithEventType:) + public static func initWithEventType(eventType: String) -> ObjCBaseEvent { + ObjCBaseEvent(eventType: eventType) + } + + @objc(initWithEventType:eventProperties:) + public static func initWithEventType(eventType: String, eventProperties: [String: Any]?) -> ObjCBaseEvent { + ObjCBaseEvent(eventType: eventType, eventProperties: eventProperties) + } + + @objc(initWithEventType:) + public convenience init(eventType: String) { + self.init(event: BaseEvent(eventType: eventType)) + } + + @objc(initWithEventType:eventProperties:) + public convenience init(eventType: String, eventProperties: [String: Any]?) { + self.init(event: BaseEvent(eventType: eventType, eventProperties: eventProperties)) + } + + internal init(event: BaseEvent) { + self.event = event + } + + @objc + public var eventType: String { + event.eventType + } + + @objc + public var eventProperties: ObjCProperties { + ObjCProperties(setter: { (key, value) in + if self.event.eventProperties == nil { + self.event.eventProperties = [:] + } + self.event.eventProperties![key] = value + }, remover: { key in + self.event.eventProperties?.removeValue(forKey: key) + }) + } + + @objc + public var userProperties: ObjCProperties { + ObjCProperties(setter: { (key, value) in + if self.event.userProperties == nil { + self.event.userProperties = [:] + } + self.event.userProperties![key] = value + }, remover: { key in + self.event.userProperties?.removeValue(forKey: key) + }) + } + + @objc + public var groups: ObjCProperties { + ObjCProperties(setter: { (key, value) in + if self.event.groups == nil { + self.event.groups = [:] + } + self.event.groups![key] = value + }, remover: { key in + self.event.groups?.removeValue(forKey: key) + }) + } + + @objc + public var groupProperties: ObjCProperties { + ObjCProperties(setter: { (key, value) in + if self.event.groupProperties == nil { + self.event.groupProperties = [:] + } + self.event.groupProperties![key] = value + }, remover: { key in + self.event.groupProperties?.removeValue(forKey: key) + }) + } +} diff --git a/Sources/Amplitude/ObjC/ObjCConfiguration.swift b/Sources/Amplitude/ObjC/ObjCConfiguration.swift new file mode 100644 index 00000000..8cdc66d9 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCConfiguration.swift @@ -0,0 +1,275 @@ +import Foundation + +@objc(AMPConfiguration) +public class ObjCConfiguration: NSObject { + internal let configuration: Configuration + + @objc(initWithApiKey:) + public static func initWithApiKey(apiKey: String) -> ObjCConfiguration { + ObjCConfiguration(apiKey: apiKey) + } + + @objc(initWithApiKey:instanceName:) + public static func initWithApiKey(apiKey: String, instanceName: String) -> ObjCConfiguration { + ObjCConfiguration(apiKey: apiKey, instanceName: instanceName) + } + + @objc(initWithApiKey:) + public convenience init(apiKey: String) { + self.init(configuration: Configuration(apiKey: apiKey)) + } + + @objc(initWithApiKey:instanceName:) + public convenience init(apiKey: String, instanceName: String) { + self.init(configuration: Configuration(apiKey: apiKey, instanceName: instanceName)) + } + + internal init(configuration: Configuration) { + self.configuration = configuration + } + + @objc + public var apiKey: String { + configuration.apiKey + } + + @objc + public var flushQueueSize: Int { + get { + configuration.flushQueueSize + } + set(value) { + configuration.flushQueueSize = value + } + } + + @objc + public var flushIntervalMillis: Int { + get { + configuration.flushIntervalMillis + } + set(value) { + configuration.flushIntervalMillis = value + } + } + + @objc + public var instanceName: String { + configuration.instanceName + } + + @objc + public var optOut: Bool { + get { + configuration.optOut + } + set(value) { + configuration.optOut = value + } + } + + @objc + public var logLevel: LogLevelEnum { + get { + configuration.logLevel + } + set(value) { + configuration.logLevel = value + configuration.loggerProvider.logLevel = value.rawValue + } + } + + @objc + public var loggerProvider: ObjCLoggerProvider? { + get { + { (logLevel, message) in + switch logLevel { + case LogLevelEnum.ERROR.rawValue: + self.configuration.loggerProvider.error(message: message) + case LogLevelEnum.WARN.rawValue: + self.configuration.loggerProvider.warn(message: message) + case LogLevelEnum.LOG.rawValue: + self.configuration.loggerProvider.log(message: message) + case LogLevelEnum.DEBUG.rawValue: + self.configuration.loggerProvider.debug(message: message) + default: + break + } + } + } + set(value) { + if let value = value { + configuration.loggerProvider = ObjCLoggerProviderWrapper(logLevel: configuration.logLevel, logProvider: value) + } + } + } + + @objc + public var minIdLength: Int { + get { + configuration.minIdLength ?? -1 + } + set(value) { + configuration.minIdLength = value + } + } + + @objc + public var callback: ObjCEventCallback? { + get { + guard let callback = configuration.callback else { return nil } + return { (event, code, message) in callback(event.event, code, message) } + } + set(value) { + if let value = value { + configuration.callback = { (event, code, message) in + value(ObjCBaseEvent(event: event), code, message) + } + } else { + configuration.callback = nil + } + } + } + + @objc + public var partnerId: String? { + get { + configuration.partnerId + } + set(value) { + configuration.partnerId = value + } + } + + @objc + public var flushMaxRetries: Int { + get { + configuration.flushMaxRetries + } + set(value) { + configuration.flushMaxRetries = value + } + } + + @objc + public var useBatch: Bool { + get { + configuration.useBatch + } + set(value) { + configuration.useBatch = value + } + } + + @objc + public var serverZone: ServerZone { + get { + configuration.serverZone + } + set(value) { + configuration.serverZone = value + } + } + + @objc + public var serverUrl: String? { + get { + configuration.serverUrl + } + set(value) { + configuration.serverUrl = value + } + } + + @objc + public var plan: ObjCPlan? { + get { + guard let plan = configuration.plan else { return nil } + return ObjCPlan(plan) + } + set(value) { + configuration.plan = value?.plan + } + } + + @objc + public var ingestionMetadata: ObjCIngestionMetadata? { + get { + guard let ingestionMetadata = configuration.ingestionMetadata else { return nil } + return ObjCIngestionMetadata(ingestionMetadata) + } + set(value) { + configuration.ingestionMetadata = value?.ingestionMetadata + } + } + + @objc + public var enableCoppaControl: Bool { + get { + configuration.enableCoppaControl + } + set(value) { + configuration.enableCoppaControl = value + } + } + + @objc + public var trackingOptions: ObjCTrackingOptions { + get { + ObjCTrackingOptions(configuration.trackingOptions) + } + set(value) { + configuration.trackingOptions = value.options + } + } + + @objc + public var flushEventsOnClose: Bool { + get { + configuration.flushEventsOnClose + } + set(value) { + configuration.flushEventsOnClose = value + } + } + + @objc + public var minTimeBetweenSessionsMillis: Int { + get { + configuration.minTimeBetweenSessionsMillis + } + set(value) { + configuration.minTimeBetweenSessionsMillis = value + } + } + + @objc + public var defaultTracking: ObjCDefaultTrackingOptions { + get { + ObjCDefaultTrackingOptions(configuration.defaultTracking) + } + set(value) { + configuration.defaultTracking = value.options + } + } + + @objc + public var identifyBatchIntervalMillis: Int { + get { + configuration.identifyBatchIntervalMillis + } + set(value) { + configuration.identifyBatchIntervalMillis = value + } + } + + @objc + public var migrateLegacyData: Bool { + get { + configuration.migrateLegacyData + } + set(value) { + configuration.migrateLegacyData = value + } + } +} diff --git a/Sources/Amplitude/ObjC/ObjCDeepLinkOpenedEvent.swift b/Sources/Amplitude/ObjC/ObjCDeepLinkOpenedEvent.swift new file mode 100644 index 00000000..c16d9f84 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCDeepLinkOpenedEvent.swift @@ -0,0 +1,38 @@ +import Foundation + +@objc(AMPDeepLinkOpenedEvent) +public class ObjCDeepLinkOpenedEvent: ObjCBaseEvent { + @objc(initWithActivity:) + public static func initWithActivity(activity: NSUserActivity) -> ObjCDeepLinkOpenedEvent { + ObjCDeepLinkOpenedEvent(activity: activity) + } + + @objc(initWithUrl:) + public static func initWithUrl(url: String?) -> ObjCDeepLinkOpenedEvent { + ObjCDeepLinkOpenedEvent(url: url) + } + + @objc(initWithUrl:referrer:) + public static func initWithUrl(url: String?, referrer: String?) -> ObjCDeepLinkOpenedEvent { + ObjCDeepLinkOpenedEvent(url: url, referrer: referrer) + } + + @objc(initWithActivity:) + public convenience init(activity: NSUserActivity) { + self.init(event: DeepLinkOpenedEvent(activity: activity)) + } + + @objc(initWihUrl:) + public convenience init(url: String?) { + self.init(url: url, referrer: nil) + } + + @objc(initWihUrl:referrer:) + public convenience init(url: String?, referrer: String?) { + self.init(event: DeepLinkOpenedEvent(url: url, referrer: referrer)) + } + + internal init(event: DeepLinkOpenedEvent) { + super.init(event: event) + } +} diff --git a/Sources/Amplitude/ObjC/ObjCDefaultTrackingOptions.swift b/Sources/Amplitude/ObjC/ObjCDefaultTrackingOptions.swift new file mode 100644 index 00000000..4e80f46c --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCDefaultTrackingOptions.swift @@ -0,0 +1,55 @@ +import Foundation + +@objc(AMPDefaultTrackingOptions) +public class ObjCDefaultTrackingOptions: NSObject { + internal let options: DefaultTrackingOptions + + @objc + convenience public override init() { + self.init(DefaultTrackingOptions()) + } + + internal init(_ options: DefaultTrackingOptions) { + self.options = options + } + + @objc + public static var ALL: ObjCDefaultTrackingOptions { + ObjCDefaultTrackingOptions(DefaultTrackingOptions.ALL) + } + + @objc + public static var NONE: ObjCDefaultTrackingOptions { + ObjCDefaultTrackingOptions(DefaultTrackingOptions.NONE) + } + + @objc + public var sessions: Bool { + get { + options.sessions + } + set(value) { + options.sessions = value + } + } + + @objc + public var appLifecycles: Bool { + get { + options.appLifecycles + } + set(value) { + options.appLifecycles = value + } + } + + @objc + public var screenViews: Bool { + get { + options.screenViews + } + set(value) { + options.screenViews = value + } + } +} diff --git a/Sources/Amplitude/ObjC/ObjCEventOptions.swift b/Sources/Amplitude/ObjC/ObjCEventOptions.swift new file mode 100644 index 00000000..67bf1e5d --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCEventOptions.swift @@ -0,0 +1,398 @@ +import Foundation + +public typealias ObjCEventCallback = (ObjCBaseEvent, Int, String) -> Void + +@objc(AMPEventOptions) +public class ObjCEventOptions: NSObject { + internal let _options = EventOptions() + + internal var options: EventOptions { + _options + } + + @objc + public var userId: String? { + get { + options.userId + } + set(value) { + options.userId = value + } + } + + @objc + public var deviceId: String? { + get { + options.deviceId + } + set(value) { + options.deviceId = value + } + } + + @objc + public var timestamp: Int64 { + get { + options.timestamp ?? -1 + } + set(value) { + options.timestamp = value + } + } + + @objc + public var eventId: Int64 { + get { + options.eventId ?? -1 + } + set(value) { + options.eventId = value + } + } + + @objc + public var sessionId: Int64 { + get { + options.sessionId ?? -1 + } + set(value) { + options.sessionId = value + } + } + + @objc + public var insertId: String? { + get { + options.insertId + } + set(value) { + options.insertId = value + } + } + + @objc + public var locationLat: Double { + get { + options.locationLat ?? Double.nan + } + set(value) { + options.locationLat = value.isNaN ? nil : value + } + } + + @objc + public var locationLng: Double { + get { + options.locationLng ?? Double.nan + } + set(value) { + options.locationLng = value.isNaN ? nil : value + } + } + + @objc + public var appVersion: String? { + get { + options.appVersion + } + set(value) { + options.appVersion = value + } + } + + @objc + public var versionName: String? { + get { + options.versionName + } + set(value) { + options.versionName = value + } + } + + @objc + public var platform: String? { + get { + options.platform + } + set(value) { + options.platform = value + } + } + + @objc + public var osName: String? { + get { + options.osName + } + set(value) { + options.osName = value + } + } + + @objc + public var osVersion: String? { + get { + options.osVersion + } + set(value) { + options.osVersion = value + } + } + + @objc + public var deviceBrand: String? { + get { + options.deviceBrand + } + set(value) { + options.deviceBrand = value + } + } + + @objc + public var deviceManufacturer: String? { + get { + options.deviceManufacturer + } + set(value) { + options.deviceManufacturer = value + } + } + + @objc + public var deviceModel: String? { + get { + options.deviceModel + } + set(value) { + options.deviceModel = value + } + } + + @objc + public var carrier: String? { + get { + options.carrier + } + set(value) { + options.carrier = value + } + } + + @objc + public var country: String? { + get { + options.country + } + set(value) { + options.country = value + } + } + + @objc + public var region: String? { + get { + options.region + } + set(value) { + options.region = value + } + } + + @objc + public var city: String? { + get { + options.city + } + set(value) { + options.city = value + } + } + + @objc + public var dma: String? { + get { + options.dma + } + set(value) { + options.dma = value + } + } + + @objc + public var idfa: String? { + get { + options.idfa + } + set(value) { + options.idfa = value + } + } + + @objc + public var idfv: String? { + get { + options.idfv + } + set(value) { + options.idfv = value + } + } + + @objc + public var adid: String? { + get { + options.adid + } + set(value) { + options.adid = value + } + } + + @objc + public var language: String? { + get { + options.language + } + set(value) { + options.language = value + } + } + + @objc + public var library: String? { + get { + options.library + } + set(value) { + options.library = value + } + } + + @objc + public var ip: String? { + get { + options.ip + } + set(value) { + options.ip = value + } + } + + @objc + public var plan: ObjCPlan? { + get { + guard let plan = options.plan else { return nil } + return ObjCPlan(plan) + } + set(value) { + options.plan = value?.plan + } + } + + @objc + public var ingestionMetadata: ObjCIngestionMetadata? { + get { + guard let ingestionMetadata = options.ingestionMetadata else { return nil } + return ObjCIngestionMetadata(ingestionMetadata) + } + set(value) { + options.ingestionMetadata = value?.ingestionMetadata + } + } + + @objc + public var revenue: Double { + get { + options.revenue ?? Double.nan + } + set(value) { + options.revenue = value.isNaN ? nil : value + } + } + + @objc + public var price: Double { + get { + options.price ?? Double.nan + } + set(value) { + options.price = value.isNaN ? nil : value + } + } + + @objc + public var quantity: Int { + get { + options.quantity ?? -1 + } + set(value) { + options.quantity = value + } + } + + @objc + public var productId: String? { + get { + options.productId + } + set(value) { + options.productId = value + } + } + + @objc + public var revenueType: String? { + get { + options.revenueType + } + set(value) { + options.revenueType = value + } + } + + @objc + public var extra: ObjCProperties { + ObjCProperties(setter: { (key, value) in + if self.options.extra == nil { + self.options.extra = [:] + } + self.options.extra![key] = value + }, remover: { key in + self.options.extra?.removeValue(forKey: key) + }) + } + + @objc + public var callback: ObjCEventCallback? { + get { + guard let callback = options.callback else { return nil } + return { (event, code, message) in callback(event.event, code, message) } + } + set(value) { + if let value = value { + options.callback = { (event, code, message) in + value(ObjCBaseEvent(event: event), code, message) + } + } else { + options.callback = nil + } + } + } + + @objc + public var partnerId: String? { + get { + options.partnerId + } + set(value) { + options.partnerId = value + } + } + + @objc(mergeEventOptions:) + public func mergeEventOptions(other: ObjCEventOptions) { + options.mergeEventOptions(eventOptions: other.options) + } +} diff --git a/Sources/Amplitude/ObjC/ObjCIdentify.swift b/Sources/Amplitude/ObjC/ObjCIdentify.swift new file mode 100644 index 00000000..80e91f24 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCIdentify.swift @@ -0,0 +1,97 @@ +import Foundation + +@objc(AMPIdentify) +public class ObjCIdentify: NSObject { + internal let identify = Identify() + + @objc(set:value:) + @discardableResult + public func set(property: String, value: Any?) -> ObjCIdentify { + identify.set(property: property, value: value) + return self + } + + @objc(setOnce:value:) + @discardableResult + public func setOnce(property: String, value: Any?) -> ObjCIdentify { + identify.setOnce(property: property, value: value) + return self + } + + @objc(prepend:value:) + @discardableResult + public func prepend(property: String, value: Any?) -> ObjCIdentify { + identify.prepend(property: property, value: value) + return self + } + + @objc(append:value:) + @discardableResult + public func append(property: String, value: Any?) -> ObjCIdentify { + identify.append(property: property, value: value) + return self + } + + @objc(postInsert:value:) + @discardableResult + public func postInsert(property: String, value: Any?) -> ObjCIdentify { + identify.postInsert(property: property, value: value) + return self + } + + @objc(preInsert:value:) + @discardableResult + public func preInsert(property: String, value: Any?) -> ObjCIdentify { + identify.preInsert(property: property, value: value) + return self + } + + @objc(remove:value:) + @discardableResult + public func remove(property: String, value: Any?) -> ObjCIdentify { + identify.remove(property: property, value: value) + return self + } + + @objc(add:valueInt:) + @discardableResult + public func add(property: String, value: Int) -> ObjCIdentify { + identify.add(property: property, value: value) + return self + } + + @objc(add:valueInt64:) + @discardableResult + public func add(property: String, value: Int64) -> ObjCIdentify { + identify.add(property: property, value: value) + return self + } + + @objc(add:valueDouble:) + @discardableResult + public func add(property: String, value: Double) -> ObjCIdentify { + identify.add(property: property, value: value) + return self + } + + @objc(add:valueFloat:) + @discardableResult + public func add(property: String, value: Float) -> ObjCIdentify { + identify.add(property: property, value: value) + return self + } + + @objc(unset:) + @discardableResult + public func unset(property: String) -> ObjCIdentify { + identify.unset(property: property) + return self + } + + @objc + @discardableResult + public func clearAll() -> ObjCIdentify { + identify.clearAll() + return self + } +} diff --git a/Sources/Amplitude/ObjC/ObjCIngestionMetadata.swift b/Sources/Amplitude/ObjC/ObjCIngestionMetadata.swift new file mode 100644 index 00000000..b02fcf34 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCIngestionMetadata.swift @@ -0,0 +1,35 @@ +import Foundation + +@objc(AMPIngestionMetadata) +public class ObjCIngestionMetadata: NSObject { + internal var ingestionMetadata: IngestionMetadata + + @objc + convenience public override init() { + self.init(IngestionMetadata()) + } + + internal init(_ ingestionMetadata: IngestionMetadata) { + self.ingestionMetadata = ingestionMetadata + } + + @objc + public var sourceName: String? { + get { + ingestionMetadata.sourceName + } + set(value) { + ingestionMetadata.sourceName = value + } + } + + @objc + public var sourceVersion: String? { + get { + ingestionMetadata.sourceVersion + } + set(value) { + ingestionMetadata.sourceVersion = value + } + } +} diff --git a/Sources/Amplitude/ObjC/ObjCLoggerProvider.swift b/Sources/Amplitude/ObjC/ObjCLoggerProvider.swift new file mode 100644 index 00000000..dd916fdc --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCLoggerProvider.swift @@ -0,0 +1,42 @@ +import Foundation + +public typealias ObjCLoggerProvider = (Int, String) -> Void + +class ObjCLoggerProviderWrapper: Logger { + public typealias LogLevel = LogLevelEnum + public var logLevel: Int + + private let logProvider: ObjCLoggerProvider + + init( + logLevel: LogLevelEnum, + logProvider: @escaping ObjCLoggerProvider + ) { + self.logLevel = logLevel.rawValue + self.logProvider = logProvider + } + + func error(message: String) { + if logLevel >= LogLevelEnum.ERROR.rawValue { + logProvider(logLevel, message) + } + } + + func warn(message: String) { + if logLevel >= LogLevelEnum.WARN.rawValue { + logProvider(logLevel, message) + } + } + + func log(message: String) { + if logLevel >= LogLevelEnum.LOG.rawValue { + logProvider(logLevel, message) + } + } + + func debug(message: String) { + if logLevel >= LogLevelEnum.DEBUG.rawValue { + logProvider(logLevel, message) + } + } +} diff --git a/Sources/Amplitude/ObjC/ObjCPlan.swift b/Sources/Amplitude/ObjC/ObjCPlan.swift new file mode 100644 index 00000000..de7effc5 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCPlan.swift @@ -0,0 +1,55 @@ +import Foundation + +@objc(AMPPlan) +public class ObjCPlan: NSObject { + internal var plan: Plan + + @objc + convenience public override init() { + self.init(Plan()) + } + + internal init(_ plan: Plan) { + self.plan = plan + } + + @objc + public var branch: String? { + get { + plan.branch + } + set(value) { + plan.branch = value + } + } + + @objc + public var source: String? { + get { + plan.source + } + set(value) { + plan.source = value + } + } + + @objc + public var version: String? { + get { + plan.version + } + set(value) { + plan.version = value + } + } + + @objc + public var versionId: String? { + get { + plan.versionId + } + set(value) { + plan.versionId = value + } + } +} diff --git a/Sources/Amplitude/ObjC/ObjCPlugin.swift b/Sources/Amplitude/ObjC/ObjCPlugin.swift new file mode 100644 index 00000000..e12b15a4 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCPlugin.swift @@ -0,0 +1,64 @@ +import Foundation + +@objc(AMPPlugin) +public class ObjCPlugin: NSObject { + internal let type: PluginType + internal let setup: ((ObjCAmplitude) -> Void)? + internal let execute: (ObjCBaseEvent) -> ObjCBaseEvent? + + @objc(initWithType:setup:execute:) + public static func initWithType( + type: PluginType, + setup: @escaping (ObjCAmplitude) -> Void, + execute: @escaping (ObjCBaseEvent) -> ObjCBaseEvent? + ) -> ObjCPlugin { + ObjCPlugin(type: type, setup: setup, execute: execute) + } + + @objc(initWithType:execute:) + public static func initWithType( + type: PluginType, + execute: @escaping (ObjCBaseEvent) -> ObjCBaseEvent? + ) -> ObjCPlugin { + ObjCPlugin(type: type, execute: execute) + } + + @objc(initWithType:setup:execute:) + public init( + type: PluginType, + setup: @escaping (ObjCAmplitude) -> Void, + execute: @escaping (ObjCBaseEvent) -> ObjCBaseEvent? + ) { + self.type = type + self.setup = setup + self.execute = execute + } + + @objc(initWithType:execute:) + public init(type: PluginType, execute: @escaping (ObjCBaseEvent) -> ObjCBaseEvent?) { + self.type = type + self.setup = nil + self.execute = execute + } +} + +class ObjCPluginWrapper: Plugin { + weak var amplitude: ObjCAmplitude? + let type: PluginType + let wrapped: ObjCPlugin + + init(amplitude: ObjCAmplitude, wrapped: ObjCPlugin) { + self.amplitude = amplitude + self.type = wrapped.type + self.wrapped = wrapped + } + + func setup(amplitude: Amplitude) { + guard let amplitude = self.amplitude else { return } + wrapped.setup?(amplitude) + } + + func execute(event: BaseEvent) -> BaseEvent? { + wrapped.execute(ObjCBaseEvent(event: event))?.event + } +} diff --git a/Sources/Amplitude/ObjC/ObjCProperties.swift b/Sources/Amplitude/ObjC/ObjCProperties.swift new file mode 100644 index 00000000..0df01b4a --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCProperties.swift @@ -0,0 +1,29 @@ +import Foundation + +@objc(AMPProperties) +public class ObjCProperties: NSObject { + internal let setter: (String, Any) -> Void + internal let remover: (String) -> Void + + internal init( + setter: @escaping (String, Any) -> Void, + remover: @escaping (String) -> Void + ) { + self.setter = setter + self.remover = remover + } + + @objc(set:value:) + @discardableResult + public func set(key: String, value: Any) -> ObjCProperties { + setter(key, value) + return self + } + + @objc(remove:) + @discardableResult + public func remove(key: String) -> ObjCProperties { + remover(key) + return self + } +} diff --git a/Sources/Amplitude/ObjC/ObjCRevenue.swift b/Sources/Amplitude/ObjC/ObjCRevenue.swift new file mode 100644 index 00000000..1648ea20 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCRevenue.swift @@ -0,0 +1,95 @@ +import Foundation + +@objc(AMPRevenue) +public class ObjCRevenue: NSObject { + internal let instance = Revenue() + + @objc + public var productId: String? { + get { + instance.productId + } + set(value) { + instance.productId = value + } + } + + @objc + public var quantity: Int { + get { + instance.quantity + } + set(value) { + instance.quantity = value + } + } + + @objc + public var price: Double { + get { + instance.price ?? Double.nan + } + set(value) { + instance.price = value.isNaN ? nil : value + } + } + + @objc + public var revenue: Double { + get { + instance.revenue ?? Double.nan + } + set(value) { + instance.revenue = value.isNaN ? nil : value + } + } + + @objc + public var revenueType: String? { + get { + instance.revenueType + } + set(value) { + instance.revenueType = value + } + } + + @objc + public var receipt: String? { + get { + instance.receipt + } + set(value) { + instance.receipt = value + } + } + + @objc + public var receiptSig: String? { + get { + instance.receiptSig + } + set(value) { + instance.receiptSig = value + } + } + + @objc + public var properties: ObjCProperties { + ObjCProperties(setter: { (key, value) in + if self.instance.properties == nil { + self.instance.properties = [:] + } + self.instance.properties![key] = value + }, remover: { key in + self.instance.properties?.removeValue(forKey: key) + }) + } + + @objc(setReceipt:receiptSignature:) + @discardableResult + public func setReceipt(receipt: String, receiptSignature: String) -> ObjCRevenue { + instance.setReceipt(receipt: receipt, receiptSignature: receiptSignature) + return self + } +} diff --git a/Sources/Amplitude/ObjC/ObjCScreenViewedEvent.swift b/Sources/Amplitude/ObjC/ObjCScreenViewedEvent.swift new file mode 100644 index 00000000..b3bd5c96 --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCScreenViewedEvent.swift @@ -0,0 +1,14 @@ +import Foundation + +@objc(AMPScreenViewedEvent) +public class ObjCScreenViewedEvent: ObjCBaseEvent { + @objc(initWithScreenName:) + public static func initWithScreenName(screenName: String) -> ObjCScreenViewedEvent { + ObjCScreenViewedEvent(screenName: screenName) + } + + @objc(initWithScreenName:) + public convenience init(screenName: String) { + self.init(event: ScreenViewedEvent(screenName: screenName)) + } +} diff --git a/Sources/Amplitude/ObjC/ObjCStorage.swift b/Sources/Amplitude/ObjC/ObjCStorage.swift new file mode 100644 index 00000000..090e3f1b --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCStorage.swift @@ -0,0 +1,29 @@ +import Foundation + +@objc(AMPStorage) +public class ObjCStorage: NSObject { + private weak var amplitude: Amplitude? + + internal init(amplitude: Amplitude) { + self.amplitude = amplitude + } + + @objc + public func getEventsStrings() -> [String] { + guard let storage = amplitude?.storage else { return [] } + return getEventsStrings(storage: storage) + } + + @objc + public func getInterceptedIdentifiesStrings() -> [String] { + guard let storage = amplitude?.identifyStorage else { return [] } + return getEventsStrings(storage: storage) + } + + private func getEventsStrings(storage: Storage) -> [String] { + guard let eventURLs: [URL] = storage.read(key: StorageKey.EVENTS) else { return [] } + return eventURLs.map { eventURL in + storage.getEventsString(eventBlock: eventURL) + }.compactMap { $0 } + } +} diff --git a/Sources/Amplitude/ObjC/ObjCTrackingOptions.swift b/Sources/Amplitude/ObjC/ObjCTrackingOptions.swift new file mode 100644 index 00000000..76eabf6c --- /dev/null +++ b/Sources/Amplitude/ObjC/ObjCTrackingOptions.swift @@ -0,0 +1,183 @@ +import Foundation + +@objc(AMPTrackingOptions) +public class ObjCTrackingOptions: NSObject { + internal let options: TrackingOptions + + @objc + convenience public override init() { + self.init(TrackingOptions()) + } + + internal init(_ options: TrackingOptions) { + self.options = options + } + + @objc + public func shouldTrackVersionName() -> Bool { + options.shouldTrackVersionName() + } + + @objc + @discardableResult + public func disableTrackVersionName() -> ObjCTrackingOptions { + options.disableTrackVersionName() + return self + } + + @objc + public func shouldTrackOsName() -> Bool { + options.shouldTrackOsName() + } + + @objc + @discardableResult + public func disableTrackOsName() -> ObjCTrackingOptions { + options.disableTrackOsName() + return self + } + + @objc + public func shouldTrackOsVersion() -> Bool { + options.shouldTrackOsVersion() + } + + @objc + @discardableResult + public func disableTrackOsVersion() -> ObjCTrackingOptions { + options.disableTrackOsVersion() + return self + } + + @objc + public func shouldTrackDeviceManufacturer() -> Bool { + options.shouldTrackDeviceManufacturer() + } + + @objc + @discardableResult + public func disableTrackDeviceManufacturer() -> ObjCTrackingOptions { + options.disableTrackDeviceManufacturer() + return self + } + + @objc + public func shouldTrackDeviceModel() -> Bool { + options.shouldTrackDeviceModel() + } + + @objc + @discardableResult + public func disableTrackDeviceModel() -> ObjCTrackingOptions { + options.disableTrackDeviceModel() + return self + } + + @objc + public func shouldTrackCarrier() -> Bool { + options.shouldTrackCarrier() + } + + @objc + @discardableResult + public func disableTrackCarrier() -> ObjCTrackingOptions { + options.disableTrackCarrier() + return self + } + + @objc + public func shouldTrackIpAddress() -> Bool { + options.shouldTrackIpAddress() + } + + @objc + @discardableResult + public func disableTrackIpAddress() -> ObjCTrackingOptions { + options.disableTrackIpAddress() + return self + } + + @objc + public func shouldTrackCountry() -> Bool { + options.shouldTrackCountry() + } + + @objc + @discardableResult + public func disableTrackCountry() -> ObjCTrackingOptions { + options.disableTrackCountry() + return self + } + + @objc + public func shouldTrackCity() -> Bool { + options.shouldTrackCity() + } + + @objc + @discardableResult + public func disableTrackCity() -> ObjCTrackingOptions { + options.disableTrackCity() + return self + } + + @objc + public func shouldTrackDMA() -> Bool { + options.shouldTrackDMA() + } + + @objc + @discardableResult + public func disableTrackDMA() -> ObjCTrackingOptions { + options.disableTrackDMA() + return self + } + + @objc + public func shouldTrackIDFV() -> Bool { + options.shouldTrackIDFV() + } + + @objc + @discardableResult + public func disableTrackIDFV() -> ObjCTrackingOptions { + options.disableTrackIDFV() + return self + } + + @objc + public func shouldTrackLanguage() -> Bool { + options.shouldTrackLanguage() + } + + @objc + @discardableResult + public func disableTrackLanguage() -> ObjCTrackingOptions { + options.disableTrackLanguage() + return self + } + + @objc + public func shouldTrackRegion() -> Bool { + options.shouldTrackRegion() + } + + @objc + @discardableResult + public func disableTrackRegion() -> ObjCTrackingOptions { + options.disableTrackRegion() + return self + } + + @objc + public func shouldTrackPlatform() -> Bool { + options.shouldTrackPlatform() + } + + @objc + @discardableResult + public func disableTrackPlatform() -> ObjCTrackingOptions { + options.disableTrackPlatform() + return self + } +} diff --git a/Sources/Amplitude/TrackingOptions.swift b/Sources/Amplitude/TrackingOptions.swift index 11b906d8..7e108737 100644 --- a/Sources/Amplitude/TrackingOptions.swift +++ b/Sources/Amplitude/TrackingOptions.swift @@ -24,6 +24,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_VERSION_NAME) } + @discardableResult public func disableTrackVersionName() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_VERSION_NAME) return self @@ -33,6 +34,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_OS_NAME) } + @discardableResult public func disableTrackOsName() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_OS_NAME) return self @@ -42,6 +44,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_OS_VERSION) } + @discardableResult public func disableTrackOsVersion() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_OS_VERSION) return self @@ -51,6 +54,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_DEVICE_MANUFACTURER) } + @discardableResult public func disableTrackDeviceManufacturer() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_DEVICE_MANUFACTURER) return self @@ -60,6 +64,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_DEVICE_MODEL) } + @discardableResult public func disableTrackDeviceModel() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_DEVICE_MODEL) return self @@ -69,7 +74,8 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_CARRIER) } - public func disableCarrier() -> TrackingOptions { + @discardableResult + public func disableTrackCarrier() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_CARRIER) return self } @@ -78,6 +84,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_IP_ADDRESS) } + @discardableResult public func disableTrackIpAddress() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_IP_ADDRESS) return self @@ -87,6 +94,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_COUNTRY) } + @discardableResult public func disableTrackCountry() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_COUNTRY) return self @@ -96,6 +104,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_CITY) } + @discardableResult public func disableTrackCity() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_CITY) return self @@ -105,6 +114,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_DMA) } + @discardableResult public func disableTrackDMA() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_DMA) return self @@ -114,6 +124,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_IDFV) } + @discardableResult public func disableTrackIDFV() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_IDFV) return self @@ -123,6 +134,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_LANGUAGE) } + @discardableResult public func disableTrackLanguage() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_LANGUAGE) return self @@ -132,6 +144,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_REGION) } + @discardableResult public func disableTrackRegion() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_REGION) return self @@ -141,6 +154,7 @@ public class TrackingOptions { return shouldTrackField(field: Constants.AMP_TRACKING_OPTION_PLATFORM) } + @discardableResult public func disableTrackPlatform() -> TrackingOptions { disabledFields.insert(Constants.AMP_TRACKING_OPTION_PLATFORM) return self diff --git a/Sources/Amplitude/Types.swift b/Sources/Amplitude/Types.swift index 4ca3b3df..2cd0669f 100644 --- a/Sources/Amplitude/Types.swift +++ b/Sources/Amplitude/Types.swift @@ -85,12 +85,13 @@ public protocol Logger { func debug(message: String) } -public enum PluginType: String, CaseIterable { - case before = "Before" - case enrichment = "Enrichment" - case destination = "Destination" - case utility = "Utility" - case observe = "Observe" +@objc(AMPPluginType) +public enum PluginType: Int, CaseIterable { + case before + case enrichment + case destination + case utility + case observe } public protocol Plugin: AnyObject { diff --git a/Sources/Amplitude/Utilities/CodableExtension.swift b/Sources/Amplitude/Utilities/CodableExtension.swift index 13912a16..181a007a 100644 --- a/Sources/Amplitude/Utilities/CodableExtension.swift +++ b/Sources/Amplitude/Utilities/CodableExtension.swift @@ -118,8 +118,20 @@ extension KeyedEncodingContainer { } var container = self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key) for item in safeValue { - if let val = item.value as? Encodable { - try container.encode(val, forKey: JSONCodingKeys(stringValue: item.key)!) + if let val = item.value as? Int { + try container.encodeIfPresent(val, forKey: JSONCodingKeys(stringValue: item.key)!) + } else if let val = item.value as? Int32 { + try container.encodeIfPresent(val, forKey: JSONCodingKeys(stringValue: item.key)!) + } else if let val = item.value as? Int64 { + try container.encodeIfPresent(val, forKey: JSONCodingKeys(stringValue: item.key)!) + } else if let val = item.value as? String { + try container.encodeIfPresent(val, forKey: JSONCodingKeys(stringValue: item.key)!) + } else if let val = item.value as? Double { + try container.encodeIfPresent(val, forKey: JSONCodingKeys(stringValue: item.key)!) + } else if let val = item.value as? Float { + try container.encodeIfPresent(val, forKey: JSONCodingKeys(stringValue: item.key)!) + } else if let val = item.value as? Bool { + try container.encodeIfPresent(val, forKey: JSONCodingKeys(stringValue: item.key)!) } else if let val = item.value as? [Any] { try container.encodeIfPresent(val, forKey: JSONCodingKeys(stringValue: item.key)!) } else if let val = item.value as? [String: Any] { diff --git a/Tests/AmplitudeTests/AmplitudeTests.swift b/Tests/AmplitudeTests/AmplitudeTests.swift index 8be1bca5..03d3ace3 100644 --- a/Tests/AmplitudeTests/AmplitudeTests.swift +++ b/Tests/AmplitudeTests/AmplitudeTests.swift @@ -86,7 +86,7 @@ final class AmplitudeTests: XCTestCase { let apiKey = "testApiKeyForDisableTrackingOptions" let trackingOptions = TrackingOptions() _ = trackingOptions.disableTrackIpAddress() - .disableCarrier() + .disableTrackCarrier() .disableTrackIDFV() .disableTrackCountry() let configuration = Configuration(apiKey: apiKey, trackingOptions: trackingOptions) diff --git a/Tests/AmplitudeTests/Events/IdentifyTests.swift b/Tests/AmplitudeTests/Events/IdentifyTests.swift index 6e76703d..c6368162 100644 --- a/Tests/AmplitudeTests/Events/IdentifyTests.swift +++ b/Tests/AmplitudeTests/Events/IdentifyTests.swift @@ -74,7 +74,7 @@ final class IdentifyTests: XCTestCase { func testUnsetOperation_withValidValue() { let identify = Identify() - identify.unset(proprety: "test-property") + identify.unset(property: "test-property") XCTAssertEqual( identify.propertySet, Set(["test-property"])