Skip to content
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

Merged

Conversation

nakajima
Copy link
Contributor

@nakajima nakajima commented Apr 3, 2024

I believe this PR brings things in line with the JSON payload described in the docs.

This makes the APNSLiveActivityNotificationEvent type a bit more clunky to deal with, so I'd love to hear if anyone has any ideas on how to improve that.

I also ran swift-format --configuration .swift-format on my changes which seems to have brought in some unrelated format changes. If this is a PR worth pursuing I'm happy to clean those up.

See also #191.

@nakajima
Copy link
Contributor Author

nakajima commented Apr 3, 2024

Ok actually it looks like I need to include an alert as well. On it.

Done in 93565bd.

@nakajima
Copy link
Contributor Author

nakajima commented Apr 3, 2024

OK, I've verified that this is working in a test app so it's ready for review!

Copy link
Contributor

@FranzBusch FranzBusch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. I left a few comments on the PR. Mostly this is a breaking API change so we have to do it slightly differently to avoid that breakage.

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

@@ -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>:
Copy link
Contributor

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.

return APNSLiveActivityNotificationEventEnd()
case "update":
return APNSLiveActivityNotificationEventUpdate()
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be exhaustive here and avoid using default cases if possible.

@nakajima
Copy link
Contributor Author

nakajima commented Apr 4, 2024

@FranzBusch Thanks so much for the review! I've moved things around to just introduce a separate object for the additional data a start event needs. I think the one downside to this approach is that if you pass a start event without the startOptions, the push just won't get sent. I don't think that's the end of the world though since that's also what APNS does afaik.

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.

Fixed in 5af6d92 and fffc9ea.

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.

Fixed in 4671d0a.

@nakajima nakajima requested a review from FranzBusch April 4, 2024 19:19
@nakajima
Copy link
Contributor Author

nakajima commented Apr 4, 2024

Actually this PR is wrong. I think we need to pass the ActivityAttributes as the attributes field, not the content state.

@nakajima
Copy link
Contributor Author

nakajima commented Apr 4, 2024

Ok I've reworked things to support sending the correct attributes value. I added a new type for start notifications because otherwise the existing types would have to know about an Attributes generic value, which I believe would be a breaking change?

@@ -152,7 +151,7 @@ public struct APNSLiveActivityNotification<ContentState: Encodable>: APNSMessage
) {
self.aps = APNSLiveActivityNotificationAPSStorage(
timestamp: timestamp,
event: event.rawValue,
event: event.rawValue,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just before this gets merged, funky indentation here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2d41546.

/// - attributes: The ActivityAttributes of the live activity to start
/// - attributesString: The type name of the ActivityAttributes you want to send
/// - alert: An alert that will be sent along with the notification
public init(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need two inits?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I don't think we do, removed in a142abe.

Copy link
Contributor

@FranzBusch FranzBusch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just one question

@nakajima nakajima requested a review from FranzBusch May 7, 2024 15:59
@chrisschlitt
Copy link

chrisschlitt commented May 11, 2024

Can confirm this works, was able to start a live activity from a push notification with:

let notification = APNSStartLiveActivityNotification(
    expiration: .immediately,
    priority: .immediately,
    appID: "com.example.example",
    contentState: TestContentState(),
    timestamp: 1_672_680_658,
    attributes: TestAttributes(),
    attributesType: "TestAttributes",
    alert: .init(title: .raw("Test"), body: .raw("Test"))
)
do {
    let response = try await apnsClient.send(
        APNSRequest(
            message: notification,
            deviceToken: kit.token,
            pushType: .liveactivity,
            expiration: notification.expiration,
            priority: notification.priority,
            apnsID: notification.apnsID,
            topic: notification.topic,
            collapseID: nil
        )
    )
    // Success
} catch {
    // Failure
}

@alex-taffe
Copy link

I know this is somewhat off topic, but while everyone is here and I can solicit advice, Apple's docs state:

When the system receives the ActivityKit push notification on a device, it starts a new Live Activity, wakes up your app, and grants it background run time to allow you to download assets that the Live Activity needs.

Does anyone know what method is invoked or what happens when the start notification goes out? I'm severely doubting it's just application:didFinishLaunchingWithOptions: but I could be wrong. I'm going to play around with it because I need it to download image assets, but just figured I'd just check to see if anyone has already figured it out

@chrisschlitt
Copy link

I know this is somewhat off topic, but while everyone is here and I can solicit advice, Apple's docs state:

When the system receives the ActivityKit push notification on a device, it starts a new Live Activity, wakes up your app, and grants it background run time to allow you to download assets that the Live Activity needs.

Does anyone know what method is invoked or what happens when the start notification goes out? I'm severely doubting it's just application:didFinishLaunchingWithOptions: but I could be wrong. I'm going to play around with it because I need it to download image assets, but just figured I'd just check to see if anyone has already figured it out

I only recently added an app delegate (I’m in a SwiftUI app), so I’m not sure which handler gets called there yet, but in SwiftUI I noticed that the onChange handler for scenePhase was triggered with the .background value, where I’m iterating over ActivityKit’s static activities property to grab the push tokens and sending them to my backend.

@kylebrowning kylebrowning merged commit 7cfeec8 into swift-server-community:main Jul 4, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants