Skip to content

Commit b44c6c6

Browse files
committed
Add widget extension methods for click tracking
1 parent 267545f commit b44c6c6

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
3C2D8A5928B4C4E300BE41F6 /* OSDelta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C2D8A5828B4C4E300BE41F6 /* OSDelta.swift */; };
7777
3C2DB2F12DE6CB5E0006B905 /* OneSignalBadgeHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C2DB2EF2DE6CB5E0006B905 /* OneSignalBadgeHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; };
7878
3C2DB2F22DE6CB5E0006B905 /* OneSignalBadgeHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C2DB2F02DE6CB5E0006B905 /* OneSignalBadgeHelpers.m */; };
79+
3C3D8D782E92DB7500C3E977 /* OSLiveActivityViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3D8D772E92DB7500C3E977 /* OSLiveActivityViewExtensions.swift */; };
7980
3C44673E296D099D0039A49E /* OneSignalMobileProvision.m in Sources */ = {isa = PBXBuildFile; fileRef = 912411FD1E73342200E41FD7 /* OneSignalMobileProvision.m */; };
8081
3C44673F296D09CC0039A49E /* OneSignalMobileProvision.h in Headers */ = {isa = PBXBuildFile; fileRef = 912411FC1E73342200E41FD7 /* OneSignalMobileProvision.h */; settings = {ATTRIBUTES = (Public, ); }; };
8182
3C448B9D2936ADFD002F96BC /* OSBackgroundTaskHandlerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C448B9B2936ADFD002F96BC /* OSBackgroundTaskHandlerImpl.h */; };
@@ -1261,6 +1262,7 @@
12611262
3C2D8A5828B4C4E300BE41F6 /* OSDelta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSDelta.swift; sourceTree = "<group>"; };
12621263
3C2DB2EF2DE6CB5E0006B905 /* OneSignalBadgeHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalBadgeHelpers.h; sourceTree = "<group>"; };
12631264
3C2DB2F02DE6CB5E0006B905 /* OneSignalBadgeHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalBadgeHelpers.m; sourceTree = "<group>"; };
1265+
3C3D8D772E92DB7500C3E977 /* OSLiveActivityViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSLiveActivityViewExtensions.swift; sourceTree = "<group>"; };
12641266
3C448B9B2936ADFD002F96BC /* OSBackgroundTaskHandlerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSBackgroundTaskHandlerImpl.h; sourceTree = "<group>"; };
12651267
3C448B9C2936ADFD002F96BC /* OSBackgroundTaskHandlerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OSBackgroundTaskHandlerImpl.m; sourceTree = "<group>"; };
12661268
3C448BA12936B474002F96BC /* OSBackgroundTaskManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSBackgroundTaskManager.swift; sourceTree = "<group>"; };
@@ -2272,6 +2274,7 @@
22722274
3CFA8F482E9087DB00201FE5 /* Requests */,
22732275
3CFA8F4B2E9087DB00201FE5 /* OneSignalLiveActivitiesManagerImpl.swift */,
22742276
3CFA8F4C2E9087DB00201FE5 /* OneSignalLiveActivityAttributes.swift */,
2277+
3C3D8D772E92DB7500C3E977 /* OSLiveActivityViewExtensions.swift */,
22752278
3CFA8F4D2E9087DB00201FE5 /* OSLiveActivitiesExtension.swift */,
22762279
3CFA8F492E9087DB00201FE5 /* AnyCodable.swift */,
22772280
3CFA8F4A2E9087DB00201FE5 /* DefaultLiveActivityAttributes.swift */,
@@ -4307,6 +4310,7 @@
43074310
buildActionMask = 2147483647;
43084311
files = (
43094312
3CFA8F4F2E9087DB00201FE5 /* AnyCodable.swift in Sources */,
4313+
3C3D8D782E92DB7500C3E977 /* OSLiveActivityViewExtensions.swift in Sources */,
43104314
3CFA8F502E9087DB00201FE5 /* OSLiveActivitiesExecutor.swift in Sources */,
43114315
3CFA8F512E9087DB00201FE5 /* DefaultLiveActivityAttributes.swift in Sources */,
43124316
3CFA8F522E9087DB00201FE5 /* OSRequestSetStartToken.swift in Sources */,
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Modified MIT License
3+
4+
Copyright 2025 OneSignal
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
1. The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
2. All copies of substantial portions of the Software may only be used in connection
17+
with services provided by OneSignal.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
*/
27+
28+
import WidgetKit
29+
import ActivityKit
30+
import SwiftUI
31+
32+
@available(iOS 16.1, *)
33+
extension DynamicIsland {
34+
/// Sets the URL that opens the corresponding app of a Live Activity when a user taps on the Live Activity.
35+
/// Sets OneSignal activity metadata. See Important callout below on usage.
36+
///
37+
/// By setting the URL with this function, it becomes the default URL for deep linking into the app
38+
/// for each view of the Live Activity. However, if you include a
39+
/// <doc://com.apple.documentation/documentation/swiftui/link> in the Live Activity,
40+
/// the link takes priority over the default URL. When a person taps on the `Link`, it takes them to the
41+
/// place in the app that corresponds to the URL of the `Link`.
42+
///
43+
/// - Parameters:
44+
/// - url: The URL that opens the app.
45+
/// - context: The activity view context.
46+
///
47+
/// - Returns: The configuration object for the Dynamic Island with the specified URL.
48+
///
49+
/// > Important: Use instead of`.widgetURL`. Requires handling from your app's URL handling code
50+
/// (e.g., `application(_:open:options:)` in AppDelegate or `onOpenURL` in SwiftUI) using the
51+
/// `OneSignal.LiveActivities.trackClickAndReturnOriginal(url)` method.
52+
public func onesignalWidgetURL<T: OneSignalLiveActivityAttributes>(
53+
_ url: URL?,
54+
context: ActivityViewContext<T>
55+
) -> DynamicIsland {
56+
return self.widgetURL(generateTrackingDeepLink(originalURL: url, context: context))
57+
}
58+
}
59+
60+
@available(iOS 16.1, *)
61+
extension View {
62+
/// Sets the URL to open in the containing app when the user clicks the widget.
63+
/// Sets OneSignal activity metadata. See Important callout below on usage.
64+
///
65+
/// - Parameters:
66+
/// - url: The URL to open in the containing app.
67+
/// - context: The activity view context.
68+
/// - Returns: A view that opens the specified URL when the user clicks
69+
/// the widget.
70+
///
71+
/// Widgets support one `onesignalWidgetURL` modifier in their view hierarchy.
72+
/// If multiple views have `onesignalWidgetURL` modifiers, the behavior is undefined.
73+
///
74+
/// > Important: Use instead of`.widgetURL`. Requires handling from your app's URL handling code
75+
/// (e.g., `application(_:open:options:)` in AppDelegate or `onOpenURL` in SwiftUI) using the
76+
/// `OneSignal.LiveActivities.trackClickAndReturnOriginal(url)` method.
77+
public func onesignalWidgetURL<T: OneSignalLiveActivityAttributes>(_ url: URL?, context: ActivityViewContext<T>) -> some View {
78+
return self.widgetURL(generateTrackingDeepLink(originalURL: url, context: context))
79+
}
80+
}
81+
82+
// MARK: - Helper Function
83+
84+
@available(iOS 16.1, *)
85+
private func generateTrackingDeepLink<T: OneSignalLiveActivityAttributes>(originalURL: URL?, context: ActivityViewContext<T>) -> URL? {
86+
// Generate a unique click ID
87+
let clickId = UUID().uuidString
88+
89+
// Get activity metadata
90+
let activityId = context.attributes.onesignal.activityId
91+
let activityType = String(describing: T.self)
92+
93+
// Build OneSignal tracking URL
94+
var components = URLComponents()
95+
components.scheme = "onesignal-liveactivity-click"
96+
components.host = "track"
97+
components.path = "/click"
98+
99+
var queryItems = [
100+
URLQueryItem(name: "clickId", value: clickId),
101+
URLQueryItem(name: "activityId", value: activityId),
102+
URLQueryItem(name: "activityType", value: activityType)
103+
]
104+
105+
if let originalURL = originalURL {
106+
queryItems.append(URLQueryItem(name: "redirect", value: originalURL.absoluteString))
107+
}
108+
109+
components.queryItems = queryItems
110+
111+
return components.url
112+
}

0 commit comments

Comments
 (0)