diff --git a/Examples/apps/MacExample/MacExample.xcodeproj/project.pbxproj b/Examples/apps/MacExample/MacExample.xcodeproj/project.pbxproj index f9840f3c..328fc02e 100644 --- a/Examples/apps/MacExample/MacExample.xcodeproj/project.pbxproj +++ b/Examples/apps/MacExample/MacExample.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 4663C73C267A926C00ADDD1A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4663C73B267A926C00ADDD1A /* Assets.xcassets */; }; 4663C73F267A926C00ADDD1A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4663C73D267A926C00ADDD1A /* Main.storyboard */; }; 4663C749267A92E100ADDD1A /* Segment in Frameworks */ = {isa = PBXBuildFile; productRef = 4663C748267A92E100ADDD1A /* Segment */; }; + 46E73DA926F53C570021042C /* NotificationTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46E73DA826F53C570021042C /* NotificationTracking.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -22,6 +23,7 @@ 4663C73E267A926C00ADDD1A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 4663C740267A926C00ADDD1A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4663C741267A926C00ADDD1A /* MacExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MacExample.entitlements; sourceTree = ""; }; + 46E73DA826F53C570021042C /* NotificationTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NotificationTracking.swift; path = ../../other_plugins/NotificationTracking.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -39,6 +41,7 @@ 4663C72B267A926B00ADDD1A = { isa = PBXGroup; children = ( + 46E73DA726F53C3C0021042C /* Plugins */, 4663C736267A926B00ADDD1A /* MacExample */, 4663C735267A926B00ADDD1A /* Products */, 4663C747267A92E100ADDD1A /* Frameworks */, @@ -73,6 +76,14 @@ name = Frameworks; sourceTree = ""; }; + 46E73DA726F53C3C0021042C /* Plugins */ = { + isa = PBXGroup; + children = ( + 46E73DA826F53C570021042C /* NotificationTracking.swift */, + ); + name = Plugins; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -147,6 +158,7 @@ files = ( 4663C73A267A926B00ADDD1A /* ViewController.swift in Sources */, 4663C738267A926B00ADDD1A /* AppDelegate.swift in Sources */, + 46E73DA926F53C570021042C /* NotificationTracking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Examples/apps/MacExample/MacExample/AppDelegate.swift b/Examples/apps/MacExample/MacExample/AppDelegate.swift index da6694c9..6f5f5402 100644 --- a/Examples/apps/MacExample/MacExample/AppDelegate.swift +++ b/Examples/apps/MacExample/MacExample/AppDelegate.swift @@ -22,7 +22,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { .flushInterval(10) analytics = Analytics(configuration: configuration) - + //analytics?.add(plugin: NotificationTracking()) } func applicationWillTerminate(_ aNotification: Notification) { diff --git a/Examples/apps/SegmentUIKitExample/SegmentUIKitExample.xcodeproj/project.pbxproj b/Examples/apps/SegmentUIKitExample/SegmentUIKitExample.xcodeproj/project.pbxproj index 459a88bd..f0536cac 100644 --- a/Examples/apps/SegmentUIKitExample/SegmentUIKitExample.xcodeproj/project.pbxproj +++ b/Examples/apps/SegmentUIKitExample/SegmentUIKitExample.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 46E3835326582DA400BA2502 /* CustomScreenTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46E3835126582DA400BA2502 /* CustomScreenTracking.swift */; }; 46E3835426582DA400BA2502 /* MultiInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46E3835226582DA400BA2502 /* MultiInstance.swift */; }; 46E383572658307800BA2502 /* Segment in Frameworks */ = {isa = PBXBuildFile; productRef = 46E383562658307800BA2502 /* Segment */; }; + 46E73DA326F531320021042C /* NotificationTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46E73DA226F531320021042C /* NotificationTracking.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -39,6 +40,7 @@ 46E3834B26582D9E00BA2502 /* ConsoleLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = ""; }; 46E3835126582DA400BA2502 /* CustomScreenTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomScreenTracking.swift; sourceTree = ""; }; 46E3835226582DA400BA2502 /* MultiInstance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiInstance.swift; sourceTree = ""; }; + 46E73DA226F531320021042C /* NotificationTracking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTracking.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -92,6 +94,7 @@ isa = PBXGroup; children = ( 46E3834826582D9E00BA2502 /* UIKitScreenTracking.swift */, + 46E73DA226F531320021042C /* NotificationTracking.swift */, 46E3834926582D9E00BA2502 /* ConsentTracking.swift */, 46E3834A26582D9E00BA2502 /* IDFACollection.swift */, 46E3834B26582D9E00BA2502 /* ConsoleLogger.swift */, @@ -200,6 +203,7 @@ 46E3834C26582D9E00BA2502 /* UIKitScreenTracking.swift in Sources */, 46E3835426582DA400BA2502 /* MultiInstance.swift in Sources */, 46E3834E26582D9E00BA2502 /* IDFACollection.swift in Sources */, + 46E73DA326F531320021042C /* NotificationTracking.swift in Sources */, 46022787261F860100A9E913 /* SceneDelegate.swift in Sources */, 46E3834D26582D9E00BA2502 /* ConsentTracking.swift in Sources */, ); diff --git a/Examples/apps/SegmentUIKitExample/SegmentUIKitExample/AppDelegate.swift b/Examples/apps/SegmentUIKitExample/SegmentUIKitExample/AppDelegate.swift index 7d34eef2..bb966a22 100644 --- a/Examples/apps/SegmentUIKitExample/SegmentUIKitExample/AppDelegate.swift +++ b/Examples/apps/SegmentUIKitExample/SegmentUIKitExample/AppDelegate.swift @@ -17,6 +17,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Analytics.main.add(plugin: ConsentTracking()) Analytics.main.add(plugin: IDFACollection()) Analytics.main.add(plugin: UIKitScreenTracking()) + Analytics.main.add(plugin: NotificationTracking()) Analytics.support.add(plugin: ConsoleLogger(name: "support")) Analytics.support.add(plugin: ConsentTracking()) diff --git a/Examples/apps/watchOSExample/watchOSExample WatchKit Extension/ExtensionDelegate.swift b/Examples/apps/watchOSExample/watchOSExample WatchKit Extension/ExtensionDelegate.swift index 0d3b9f2c..9a20f3f5 100644 --- a/Examples/apps/watchOSExample/watchOSExample WatchKit Extension/ExtensionDelegate.swift +++ b/Examples/apps/watchOSExample/watchOSExample WatchKit Extension/ExtensionDelegate.swift @@ -19,6 +19,7 @@ class ExtensionDelegate: NSObject, WKExtensionDelegate { analytics = Analytics(configuration: configuration) analytics?.add(plugin: ConsoleLogger(name: "consoleLogger")) + analytics?.add(plugin: NotificationTracking()) } func applicationDidBecomeActive() { diff --git a/Examples/apps/watchOSExample/watchOSExample.xcodeproj/project.pbxproj b/Examples/apps/watchOSExample/watchOSExample.xcodeproj/project.pbxproj index ed5f0be0..f40b98f0 100644 --- a/Examples/apps/watchOSExample/watchOSExample.xcodeproj/project.pbxproj +++ b/Examples/apps/watchOSExample/watchOSExample.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 469ECD672684F9090028BE9A /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469ECD662684F9090028BE9A /* ComplicationController.swift */; }; 469ECD692684F90A0028BE9A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 469ECD682684F90A0028BE9A /* Assets.xcassets */; }; 469ECD7B2684FD710028BE9A /* Segment in Frameworks */ = {isa = PBXBuildFile; productRef = 469ECD7A2684FD710028BE9A /* Segment */; }; + 46E73DA626F5389E0021042C /* NotificationTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46E73DA526F5389E0021042C /* NotificationTracking.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -79,6 +80,7 @@ 469ECD682684F90A0028BE9A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 469ECD6A2684F90A0028BE9A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 469ECD6B2684F90A0028BE9A /* PushNotificationPayload.apns */ = {isa = PBXFileReference; lastKnownFileType = text; path = PushNotificationPayload.apns; sourceTree = ""; }; + 46E73DA526F5389E0021042C /* NotificationTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NotificationTracking.swift; path = ../../other_plugins/NotificationTracking.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -96,6 +98,7 @@ 469ECD412684F9080028BE9A = { isa = PBXGroup; children = ( + 46E73DA426F538940021042C /* Plugins */, 469ECD502684F9080028BE9A /* watchOSExample WatchKit App */, 469ECD5F2684F9090028BE9A /* watchOSExample WatchKit Extension */, 469ECD492684F9080028BE9A /* Products */, @@ -146,6 +149,14 @@ name = Frameworks; sourceTree = ""; }; + 46E73DA426F538940021042C /* Plugins */ = { + isa = PBXGroup; + children = ( + 46E73DA526F5389E0021042C /* NotificationTracking.swift */, + ); + name = Plugins; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -279,6 +290,7 @@ 465879B4268641B900180335 /* SomeScreenController.swift in Sources */, 469ECD672684F9090028BE9A /* ComplicationController.swift in Sources */, 469ECD632684F9090028BE9A /* ExtensionDelegate.swift in Sources */, + 46E73DA626F5389E0021042C /* NotificationTracking.swift in Sources */, 465879B22685058800180335 /* ConsoleLogger.swift in Sources */, 469ECD612684F9090028BE9A /* InterfaceController.swift in Sources */, ); diff --git a/Examples/other_plugins/NotificationTracking.swift b/Examples/other_plugins/NotificationTracking.swift new file mode 100644 index 00000000..6c7bd470 --- /dev/null +++ b/Examples/other_plugins/NotificationTracking.swift @@ -0,0 +1,98 @@ +// +// NotificationTracking.swift +// +// Created by Brandon Sneed on 9/17/21. +// + +// NOTE: You can see this plugin in use in the SwiftUIKitExample application. +// +// This plugin is NOT SUPPORTED by Segment. It is here merely as an example, +// and for your convenience should you find it useful. + +// MIT License +// +// Copyright (c) 2021 Segment +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// MARK: Common + +#if !os(Linux) && !os(macOS) + +import Foundation +import Segment + +class NotificationTracking: Plugin { + var type: PluginType = .utility + var analytics: Analytics? + + func trackNotification(_ properties: [String: Any], fromLaunch launch: Bool) { + if launch { + analytics?.track(name: "Push Notification Tapped", properties: properties) + } else { + analytics?.track(name: "Push Notification Received", properties: properties) + } + } +} + +// NOTE: watchOS doesn't have the concept of launch options for making a +// determination if a push notification caused the app to open. +extension NotificationTracking: RemoteNotifications { + func receivedRemoteNotification(userInfo: [AnyHashable: Any]) { + if let notification = userInfo as? [String: Any] { + trackNotification(notification, fromLaunch: false) + } + } +} + +#endif + +// MARK: macOS -- TODO: Full lifecycle/delegation options in library. +/* +#if os(macOS) + +import Cocoa + +extension NotificationTracking: macOSLifecycle { + func application(didFinishLaunchingWithOptions launchOptions: [String: Any]?) { + if let notification = launchOptions?[NSApplication.launchUserNotificationUserInfoKey] as? [String: Any] { + trackNotification(notification, fromLaunch: true) + } + } +} + +#endif +*/ + +// MARK: iOS/tvOS/Catalyst + +#if os(tvOS) || os(iOS) || targetEnvironment(macCatalyst) + +import UIKit + +extension NotificationTracking: iOSLifecycle { + func application(_ application: UIApplication?, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { + if let notification = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? [String: Any] { + trackNotification(notification, fromLaunch: true) + } + } +} + +#endif + diff --git a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift index 9d100616..3a8b3c7e 100644 --- a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift +++ b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift @@ -18,7 +18,7 @@ class iOSLifecycleEvents: PlatformPlugin, iOSLifecycle { let type = PluginType.before var analytics: Analytics? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { + func application(_ application: UIApplication?, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { if analytics?.configuration.values.trackApplicationLifecycleEvents == false { return } @@ -55,7 +55,7 @@ class iOSLifecycleEvents: PlatformPlugin, iOSLifecycle { UserDefaults.standard.setValue(currentBuild, forKey: Self.buildKey) } - func applicationWillEnterForeground(application: UIApplication) { + func applicationWillEnterForeground(application: UIApplication?) { if analytics?.configuration.values.trackApplicationLifecycleEvents == false { return } @@ -70,7 +70,7 @@ class iOSLifecycleEvents: PlatformPlugin, iOSLifecycle { ]) } - func applicationDidEnterBackground(application: UIApplication) { + func applicationDidEnterBackground(application: UIApplication?) { if analytics?.configuration.values.trackApplicationLifecycleEvents == false { return } diff --git a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleMonitor.swift b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleMonitor.swift index f9406a4a..e3287b2a 100644 --- a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleMonitor.swift +++ b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleMonitor.swift @@ -10,6 +10,8 @@ import Foundation import UIKit +// NOTE: These method signatures are marked optional as application extensions may not have +// a UIApplication object available. See `safeShared` below. public protocol iOSLifecycle { func applicationDidEnterBackground(application: UIApplication?) func applicationWillEnterForeground(application: UIApplication?)