Skip to content

Commit 0ba13bf

Browse files
brismithersemawbynan-li
authored
Push to start live activities (#1377)
Update SDK to support Live Activities PushToStart and add a concept of a "Default" Live Activity to facilitate easier wrapper SDK adoption. Push to Start Live Activities Starting with iOS 17.2, Live Activities can now be started via push notification. This change enhances the OneSignal SDK to provide application's access to the full suite of Live Activity functionality. Default Live Activity The concept of a "Default" Live Activity has been established in the SDK, which eliminates the need for a customer app to define and manage their own `ActivityAttributes`. This is most beneficial for wrapper-SDKs, as they will no longer need to create their own cross-platform <-> native iOS bridge to establish this management. --------- Co-authored-by: emawby <[email protected]> Co-authored-by: Nan <[email protected]>
1 parent 5e3d8e4 commit 0ba13bf

File tree

88 files changed

+4988
-1146
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+4988
-1146
lines changed

.github/workflows/cd.yml

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ jobs:
6161
codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_Outcomes/OneSignalOutcomes.xcframework
6262
codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_User/OneSignalUser.xcframework
6363
codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_XCFramework/OneSignalFramework.xcframework
64+
codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_LiveActivities/OneSignalLiveActivities.xcframework
6465
shell: bash
6566
- name: Update Swift Package
6667
run: |

.github/workflows/ci.yml

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ jobs:
1414
runs-on: macos-latest-large
1515

1616
steps:
17+
- name: Select Xcode Version
18+
run: |
19+
sudo xcode-select -s /Applications/Xcode_15.2.app
1720
- name: Checkout OneSignal-iOS-SDK
1821
uses: actions/checkout@v3
1922
- name: Set Default Scheme

OneSignal.podspec

+8
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ Pod::Spec.new do |s|
4646
ss.vendored_frameworks = 'iOS_SDK/OneSignalSDK/OneSignal_User/OneSignalUser.xcframework'
4747
end
4848

49+
s.subspec 'OneSignalLiveActivities' do |ss|
50+
ss.dependency 'OneSignal/OneSignalCore'
51+
ss.dependency 'OneSignal/OneSignalOSCore'
52+
ss.dependency 'OneSignal/OneSignalUser'
53+
ss.vendored_frameworks = 'iOS_SDK/OneSignalSDK/OneSignal_LiveActivities/OneSignalLiveActivities.xcframework'
54+
end
55+
4956
s.subspec 'OneSignalLocation' do |ss|
5057
ss.dependency 'OneSignal/OneSignalCore'
5158
ss.dependency 'OneSignal/OneSignalOSCore'
@@ -70,6 +77,7 @@ Pod::Spec.new do |s|
7077
ss.dependency 'OneSignal/OneSignalExtension'
7178
ss.dependency 'OneSignal/OneSignalNotifications'
7279
ss.dependency 'OneSignal/OneSignalUser'
80+
ss.dependency 'OneSignal/OneSignalLiveActivities'
7381
ss.ios.vendored_frameworks = 'iOS_SDK/OneSignalSDK/OneSignal_XCFramework/OneSignalFramework.xcframework'
7482
end
7583

OneSignalLiveActivitiesWrapper/dummy.m

Whitespace-only changes.

OneSignalLiveActivitiesWrapper/include/dummy.h

Whitespace-only changes.

OneSignalXCFramework.podspec

+8
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ Pod::Spec.new do |s|
4646
ss.vendored_frameworks = 'iOS_SDK/OneSignalSDK/OneSignal_User/OneSignalUser.xcframework'
4747
end
4848

49+
s.subspec 'OneSignalLiveActivities' do |ss|
50+
ss.dependency 'OneSignalXCFramework/OneSignalCore'
51+
ss.dependency 'OneSignalXCFramework/OneSignalOSCore'
52+
ss.dependency 'OneSignalXCFramework/OneSignalUser'
53+
ss.vendored_frameworks = 'iOS_SDK/OneSignalSDK/OneSignal_LiveActivities/OneSignalLiveActivities.xcframework'
54+
end
55+
4956
s.subspec 'OneSignalLocation' do |ss|
5057
ss.dependency 'OneSignalXCFramework/OneSignalCore'
5158
ss.dependency 'OneSignalXCFramework/OneSignalOSCore'
@@ -70,6 +77,7 @@ Pod::Spec.new do |s|
7077
ss.dependency 'OneSignalXCFramework/OneSignalExtension'
7178
ss.dependency 'OneSignalXCFramework/OneSignalNotifications'
7279
ss.dependency 'OneSignalXCFramework/OneSignalUser'
80+
ss.dependency 'OneSignalXCFramework/OneSignalLiveActivities'
7381
ss.ios.vendored_frameworks = 'iOS_SDK/OneSignalSDK/OneSignal_XCFramework/OneSignalFramework.xcframework'
7482
end
7583

Package.swift

+14
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ let package = Package(
2626
"OneSignalFramework",
2727
"OneSignalUser",
2828
"OneSignalNotifications",
29+
"OneSignalLiveActivities",
2930
"OneSignalExtension",
3031
"OneSignalOutcomes",
3132
"OneSignalOSCore",
@@ -103,6 +104,14 @@ let package = Package(
103104
],
104105
path: "OneSignalOSCoreWrapper"
105106
),
107+
.target(
108+
name: "OneSignalLiveActivitiesWrapper",
109+
dependencies: [
110+
"OneSignalUser",
111+
"OneSignalCore"
112+
],
113+
path: "OneSignalLiveActivitiesWrapper"
114+
),
106115
.binaryTarget(
107116
name: "OneSignalFramework",
108117
url: "https://github.com/OneSignal/OneSignal-iOS-SDK/releases/download/5.1.6/OneSignalFramework.xcframework.zip",
@@ -147,6 +156,11 @@ let package = Package(
147156
name: "OneSignalCore",
148157
url: "https://github.com/OneSignal/OneSignal-iOS-SDK/releases/download/5.1.6/OneSignalCore.xcframework.zip",
149158
checksum: "46be3f316ed4506ab88d787821476572c9b016d7778beaa60a59a607c6c223e7"
159+
),
160+
.binaryTarget(
161+
name: "OneSignalLiveActivities",
162+
url: "https://github.com/OneSignal/OneSignal-iOS-SDK/releases/download/5.1.0/OneSignalLiveActivities.xcframework.zip",
163+
checksum: ""
150164
)
151165
]
152166
)

default

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
UnitTestApp

iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#import "AppDelegate.h"
3232
#import "ViewController.h"
33+
#import "OneSignalExample-Swift.h"
3334

3435
@interface OneSignalNotificationCenterDelegate: NSObject<UNUserNotificationCenterDelegate>
3536
@end
@@ -74,6 +75,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
7475

7576
NSLog(@"UNUserNotificationCenter.delegate: %@", UNUserNotificationCenter.currentNotificationCenter.delegate);
7677

78+
#if TARGET_OS_MACCATALYST
79+
#else
80+
if (@available(iOS 16.1, *)) {
81+
[LiveActivityController start];
82+
}
83+
#endif
84+
85+
7786
return YES;
7887
}
7988

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2023 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+
#if targetEnvironment(macCatalyst)
28+
#else
29+
import ActivityKit
30+
import OneSignalLiveActivities
31+
32+
/**
33+
An example of an ActivityAttribute that is "OneSignal SDK aware". This attribute conforms
34+
to the OneSignalActivityAttributes protocol and has a onesignal property, which contains
35+
metadata used by the OneSignal SDK. To enable this Live Activity type for OneSignal
36+
you only need to call `enableLiveActivities`, the SDK takes care of the rest.
37+
*/
38+
struct ExampleAppFirstWidgetAttributes: OneSignalLiveActivityAttributes {
39+
public struct ContentState: OneSignalLiveActivityContentState {
40+
// Dynamic stateful properties about your activity go here!
41+
var message: String
42+
43+
var onesignal: OneSignalLiveActivityContentStateData?
44+
}
45+
46+
// Fixed non-changing properties about your activity go here!
47+
var title: String
48+
49+
// OneSignal Attributes to allow the SDK to do it's thing
50+
var onesignal: OneSignalLiveActivityAttributeData
51+
}
52+
53+
/**
54+
Another example of an ActivityAttribute that is "OneSignal SDK aware". A second attributes
55+
structure is created here because the example app has two different "types" of Live Activity
56+
experiences. Noting that `enableLiveActivities` must be called for each "type" of
57+
Live Activity defined.
58+
*/
59+
struct ExampleAppSecondWidgetAttributes: OneSignalLiveActivityAttributes {
60+
public struct ContentState: OneSignalLiveActivityContentState {
61+
var message: String
62+
var status: String
63+
var progress: Double
64+
var bugs: Int
65+
66+
var onesignal: OneSignalLiveActivityContentStateData?
67+
}
68+
69+
// Fixed non-changing properties about your activity go here!
70+
var title: String
71+
72+
// OneSignal Attributes to allow the SDK to do it's thing
73+
var onesignal: OneSignalLiveActivityAttributeData
74+
}
75+
76+
/**
77+
This example of an ActivityAttribute is **not** "OneSignal SDK aware". Listening
78+
to push to start tokens and update tokens for this activity must be done by the app
79+
itself.
80+
*/
81+
struct ExampleAppThirdWidgetAttributes: ActivityAttributes {
82+
public struct ContentState: Codable, Hashable {
83+
// Dynamic stateful properties about your activity go here!
84+
var message: String
85+
}
86+
87+
// Fixed non-changing properties about your activity go here!
88+
var title: String
89+
90+
// Whether this LA was started via pushToStart (true), or via app (false)
91+
var isPushToStart: Bool
92+
}
93+
#endif

iOS_SDK/OneSignalDevApp/OneSignalDevApp/LiveActivityController.swift

+95-20
Original file line numberDiff line numberDiff line change
@@ -26,43 +26,118 @@
2626
*/
2727

2828
import Foundation
29-
import ActivityKit
3029
import UserNotifications
30+
import OneSignalFramework
31+
#if targetEnvironment(macCatalyst)
32+
#else
33+
import ActivityKit
34+
import OneSignalLiveActivities
35+
@objc
36+
class LiveActivityController: NSObject {
37+
38+
@available(iOS 16.1, *)
39+
@objc
40+
static func start() {
41+
// ExampleAppFirstWidgetAttributes and ExampleAppSecondWidgetAttributes enable the OneSignal SDK to
42+
// listen for start/update tokens, this is the only call needed.
43+
OneSignal.LiveActivities.setup(ExampleAppFirstWidgetAttributes.self)
44+
OneSignal.LiveActivities.setup(ExampleAppSecondWidgetAttributes.self)
3145

32-
struct OneSignalWidgetAttributes: ActivityAttributes {
33-
public struct ContentState: Codable, Hashable {
34-
// Dynamic stateful properties about your activity go here!
35-
var message: String
46+
// There is a "built in" Live Activity Widget Attributes called `DefaultLiveActivityAttributes`.
47+
// This is mostly for cross-platform SDKs and allows OneSignal to handle everything but the
48+
// creation of the Widget Extension.
49+
OneSignal.LiveActivities.setupDefault()
50+
51+
if #available(iOS 17.2, *) {
52+
// ExampleAppThirdWidgetAttributes is an example of how to manually set up LA.
53+
// Setup an async task to monitor and send pushToStartToken updates to OneSignalSDK.
54+
Task {
55+
for try await data in Activity<ExampleAppThirdWidgetAttributes>.pushToStartTokenUpdates {
56+
let token = data.map {String(format: "%02x", $0)}.joined()
57+
OneSignal.LiveActivities.setPushToStartToken(ExampleAppThirdWidgetAttributes.self, withToken: token)
58+
}
59+
}
60+
// Setup an async task to monitor for an activity to be started, for each started activity we
61+
// can then set up an async task to monitor and send updateToken updates to OneSignalSDK. We
62+
// filter out LA started in-app, because the `createActivity` function below does its own
63+
// updateToken update monitoring. If there can be multiple instances of this activity-type,
64+
// the activity-id (i.e. "my-activity-id") is most likely passed down as an attribute within
65+
// ExampleAppThirdWidgetAttributes.
66+
Task {
67+
for await activity in Activity<ExampleAppThirdWidgetAttributes>.activityUpdates
68+
where activity.attributes.isPushToStart {
69+
Task {
70+
for await pushToken in activity.pushTokenUpdates {
71+
let token = pushToken.map {String(format: "%02x", $0)}.joined()
72+
OneSignal.LiveActivities.enter("my-activity-id", withToken: token)
73+
}
74+
}
75+
}
76+
}
77+
}
3678
}
3779

38-
// Fixed non-changing properties about your activity go here!
39-
var title: String
40-
}
41-
@objc
42-
class LiveActivityController: NSObject {
43-
// To aid in testing
44-
static var counter = 0
80+
/**
81+
An example of starting a Live Activity whose attributes are "OneSignal SDK aware". The SDK will handle listening for update tokens on behalf of the app.
82+
*/
83+
static var counter1 = 0
84+
@available(iOS 13.0, *)
85+
@objc
86+
static func createOneSignalAwareActivity(activityId: String) {
87+
if #available(iOS 16.1, *) {
88+
counter1 += 1
89+
let oneSignalAttribute = OneSignalLiveActivityAttributeData.create(activityId: activityId)
90+
let attributes = ExampleAppFirstWidgetAttributes(title: "#" + String(counter1) + " OneSignal Dev App Live Activity", onesignal: oneSignalAttribute)
91+
let contentState = ExampleAppFirstWidgetAttributes.ContentState(message: "Update this message through push or with Activity Kit")
92+
do {
93+
_ = try Activity<ExampleAppFirstWidgetAttributes>.request(
94+
attributes: attributes,
95+
contentState: contentState,
96+
pushType: .token)
97+
} catch let error {
98+
print(error.localizedDescription)
99+
}
100+
}
101+
}
102+
103+
/**
104+
An example of starting a Live Activity using the DefaultLiveActivityAttributes. The SDK will handle listening for update tokens on behalf of the app.
105+
*/
106+
@available(iOS 13.0, *)
107+
@objc
108+
static func createDefaultActivity(activityId: String) {
109+
if #available(iOS 16.1, *) {
110+
let attributeData: [String: Any] = ["title": "in-app-title"]
111+
let contentData: [String: Any] = ["message": ["en": "HELLO", "es": "HOLA"], "progress": 0.58, "status": "1/15", "bugs": 2]
112+
113+
OneSignal.LiveActivities.startDefault(activityId, attributes: attributeData, content: contentData)
114+
}
115+
}
116+
117+
/**
118+
An example of starting a Live Activity whose attributes are **not** "OneSignal SDK aware". The app must handle listening for update tokens and notify the OneSignal SDK.
119+
*/
120+
static var counter2 = 0
45121
@available(iOS 13.0, *)
46122
@objc
47-
static func createActivity() async -> String? {
123+
static func createActivity(activityId: String) async {
48124
if #available(iOS 16.1, *) {
49-
counter += 1
50-
let attributes = OneSignalWidgetAttributes(title: "#" + String(counter) + " OneSignal Dev App Live Activity")
51-
let contentState = OneSignalWidgetAttributes.ContentState(message: "Update this message through push or with Activity Kit")
125+
counter2 += 1
126+
let attributes = ExampleAppThirdWidgetAttributes(title: "#" + String(counter2) + " OneSignal Dev App Live Activity", isPushToStart: false)
127+
let contentState = ExampleAppThirdWidgetAttributes.ContentState(message: "Update this message through push or with Activity Kit")
52128
do {
53-
let activity = try Activity<OneSignalWidgetAttributes>.request(
129+
let activity = try Activity<ExampleAppThirdWidgetAttributes>.request(
54130
attributes: attributes,
55131
contentState: contentState,
56132
pushType: .token)
57133
for await data in activity.pushTokenUpdates {
58134
let myToken = data.map {String(format: "%02x", $0)}.joined()
59-
return myToken
135+
OneSignal.LiveActivities.enter(activityId, withToken: myToken)
60136
}
61137
} catch let error {
62138
print(error.localizedDescription)
63-
return nil
64139
}
65140
}
66-
return nil
67141
}
68142
}
143+
#endif

iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m

+5-5
Original file line numberDiff line numberDiff line change
@@ -232,19 +232,19 @@ - (IBAction)sendUniqueOutcomeEvent:(id)sender {
232232
}
233233

234234
- (IBAction)startAndEnterLiveActivity:(id)sender {
235+
#if TARGET_OS_MACCATALYST
236+
#else
235237
if (@available(iOS 13.0, *)) {
236238
NSString *activityId = [self.activityId text];
237239
// Will not make a live activity if activityId is empty
238240
if (activityId && activityId.length) {
239-
[LiveActivityController createActivityWithCompletionHandler:^(NSString * token) {
240-
if(token){
241-
[OneSignal.LiveActivities enter:activityId withToken:token];
242-
}
243-
}];
241+
// [LiveActivityController createDefaultActivityWithActivityId:activityId ];
242+
[LiveActivityController createActivityWithActivityId:activityId completionHandler:^(void) {} ];
244243
}
245244
} else {
246245
NSLog(@"Must use iOS 13 or later for swift concurrency which is required for [LiveActivityController createActivityWithCompletionHandler...");
247246
}
247+
#endif
248248
}
249249
- (IBAction)exitLiveActivity:(id)sender {
250250
if (self.activityId.text && self.activityId.text.length) {

0 commit comments

Comments
 (0)