-
-
Notifications
You must be signed in to change notification settings - Fork 104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to start live activities #196
Changes from 5 commits
3540be5
0c2bb05
d39bd0b
e20b90a
93565bd
5af6d92
fffc9ea
3d78f52
4671d0a
4fb1daf
d2cdfb8
2b43198
3818be8
f5087ba
2d41546
a142abe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,9 @@ import struct Foundation.UUID | |
/// A live activity notification. | ||
/// | ||
/// It is **important** that you do not encode anything with the key `aps`. | ||
public struct APNSLiveActivityNotification<ContentState: Encodable>: APNSMessage { | ||
public struct APNSLiveActivityNotification<ContentState: Encodable & Hashable & Sendable>: | ||
APNSMessage | ||
{ | ||
enum CodingKeys: CodingKey { | ||
case aps | ||
} | ||
|
@@ -30,34 +32,62 @@ public struct APNSLiveActivityNotification<ContentState: Encodable>: APNSMessage | |
get { | ||
return self.aps.timestamp | ||
} | ||
|
||
set { | ||
self.aps.timestamp = newValue | ||
} | ||
} | ||
|
||
|
||
public var alert: APNSAlertNotificationContent? { | ||
get { | ||
return self.aps.alert | ||
} | ||
|
||
set { | ||
self.aps.alert = newValue | ||
} | ||
} | ||
|
||
/// Event type e.g. update | ||
public var event: APNSLiveActivityNotificationEvent { | ||
public var event: any APNSLiveActivityNotificationEvent { | ||
get { | ||
return APNSLiveActivityNotificationEvent(rawValue: self.aps.event) | ||
switch self.aps.event { | ||
case "end": | ||
return APNSLiveActivityNotificationEventEnd() | ||
case "update": | ||
return APNSLiveActivityNotificationEventUpdate() | ||
default: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be exhaustive here and avoid using default cases if possible. |
||
guard let attributesType = self.aps.attributesType, | ||
let state = self.aps.attributesContent, | ||
let alert = self.aps.alert | ||
else { | ||
// Default to update | ||
return APNSLiveActivityNotificationEventUpdate() | ||
} | ||
|
||
return APNSLiveActivityNotificationEventStart( | ||
attributes: .init(type: attributesType, state: state), | ||
alert: alert | ||
) | ||
} | ||
} | ||
|
||
set { | ||
self.aps.event = newValue.rawValue | ||
} | ||
} | ||
|
||
/// The dynamic content of a Live Activity. | ||
public var contentState: ContentState { | ||
get { | ||
return self.aps.contentState | ||
} | ||
|
||
set { | ||
self.aps.contentState = newValue | ||
} | ||
} | ||
|
||
public var dismissalDate: APNSLiveActivityDismissalDate? { | ||
get { | ||
return .init(dismissal: self.aps.dismissalDate) | ||
|
@@ -66,7 +96,7 @@ public struct APNSLiveActivityNotification<ContentState: Encodable>: APNSMessage | |
self.aps.dismissalDate = newValue?.dismissal | ||
} | ||
} | ||
|
||
/// A canonical UUID that identifies the notification. If there is an error sending the notification, | ||
/// APNs uses this value to identify the notification to your server. The canonical form is 32 lowercase hexadecimal digits, | ||
/// displayed in five groups separated by hyphens in the form 8-4-4-4-12. An example UUID is as follows: | ||
|
@@ -108,7 +138,7 @@ public struct APNSLiveActivityNotification<ContentState: Encodable>: APNSMessage | |
priority: APNSPriority, | ||
appID: String, | ||
contentState: ContentState, | ||
event: APNSLiveActivityNotificationEvent, | ||
event: any APNSLiveActivityNotificationEvent, | ||
timestamp: Int, | ||
dismissalDate: APNSLiveActivityDismissalDate = .none, | ||
apnsID: UUID? = nil | ||
|
@@ -123,7 +153,6 @@ public struct APNSLiveActivityNotification<ContentState: Encodable>: APNSMessage | |
dismissalDate: dismissalDate | ||
) | ||
} | ||
|
||
|
||
/// Initializes a new ``APNSLiveActivityNotification``. | ||
/// | ||
|
@@ -146,13 +175,18 @@ public struct APNSLiveActivityNotification<ContentState: Encodable>: APNSMessage | |
topic: String, | ||
apnsID: UUID? = nil, | ||
contentState: ContentState, | ||
event: APNSLiveActivityNotificationEvent, | ||
event: any APNSLiveActivityNotificationEvent, | ||
timestamp: Int, | ||
dismissalDate: APNSLiveActivityDismissalDate = .none | ||
) { | ||
var attributes: APNSLiveActivityNotificationEventStart<ContentState>.Attributes? | ||
if let event = event as? APNSLiveActivityNotificationEventStart<ContentState> { | ||
attributes = event.attributes | ||
} | ||
|
||
self.aps = APNSLiveActivityNotificationAPSStorage( | ||
timestamp: timestamp, | ||
event: event.rawValue, | ||
event: event, | ||
contentState: contentState, | ||
dismissalDate: dismissalDate.dismissal | ||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,15 +12,65 @@ | |
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
public struct APNSLiveActivityNotificationEvent: Hashable { | ||
|
||
/// The underlying raw value that is send to APNs. | ||
@usableFromInline | ||
internal let rawValue: String | ||
|
||
/// Specifies that live activity should be updated | ||
public static let update = Self(rawValue: "update") | ||
|
||
/// Specifies that live activity should be ended | ||
public static let end = Self(rawValue: "end") | ||
public protocol APNSLiveActivityNotificationEvent: Hashable, Encodable { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a breaking change and we can't just change from a struct to a protocol. We should try and continue to model it with the current struct type. |
||
var rawValue: String { get } | ||
} | ||
|
||
public struct APNSLiveActivityNotificationEventUpdate: APNSLiveActivityNotificationEvent { | ||
public let rawValue = "update" | ||
} | ||
|
||
public struct APNSLiveActivityNotificationEventEnd: APNSLiveActivityNotificationEvent { | ||
public let rawValue = "end" | ||
} | ||
|
||
public protocol APNSLiveActivityNotificationEventStartStateProtocol: Encodable & Hashable & Sendable | ||
{ | ||
associatedtype State: Encodable & Hashable & Sendable | ||
} | ||
|
||
public struct APNSLiveActivityNotificationEventStart<State: Encodable & Hashable & Sendable>: | ||
APNSLiveActivityNotificationEvent, APNSLiveActivityNotificationEventStartStateProtocol | ||
{ | ||
public struct Attributes: Encodable, Hashable, Sendable { | ||
public let type: String | ||
public let state: State | ||
|
||
public init(type: String, state: State) { | ||
self.type = type | ||
self.state = state | ||
} | ||
} | ||
|
||
public let rawValue = "start" | ||
public let attributes: Attributes | ||
public let alert: APNSAlertNotificationContent | ||
|
||
public init(attributes: Attributes, alert: APNSAlertNotificationContent) { | ||
self.attributes = attributes | ||
self.alert = alert | ||
} | ||
} | ||
|
||
extension APNSLiveActivityNotificationEvent where Self == APNSLiveActivityNotificationEventUpdate { | ||
public static var update: APNSLiveActivityNotificationEventUpdate { | ||
APNSLiveActivityNotificationEventUpdate() | ||
} | ||
} | ||
|
||
extension APNSLiveActivityNotificationEvent where Self == APNSLiveActivityNotificationEventEnd { | ||
public static var end: APNSLiveActivityNotificationEventEnd { | ||
APNSLiveActivityNotificationEventEnd() | ||
} | ||
} | ||
|
||
extension APNSLiveActivityNotificationEvent | ||
where Self: APNSLiveActivityNotificationEventStartStateProtocol { | ||
public static func start(type: String, state: State, alert: APNSAlertNotificationContent) | ||
-> APNSLiveActivityNotificationEventStart< | ||
State | ||
> | ||
{ | ||
.init(attributes: .init(type: type, state: state), alert: alert) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't just add these requirements to the generic type. This is a breaking API change otherwise. We can make the conformance to those protocols conditional though and that should work.