feat: Support Apple push live activity notifications#184
feat: Support Apple push live activity notifications#184
Conversation
📝 WalkthroughSummary by CodeRabbitRelease Notes
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughPLUS ULTRA! These changes fortify the Apple push notification infrastructure by introducing structured protocols and types for payload management. Four new Swift files wire protocols ParsePushAppleHeader and ParsePushApplePayload, types ParsePushNotificationBody, ParsePushAppleNotification, and ParsePushPayloadAppleLiveActivity into the codebase, alongside refactoring existing payload types with enhanced serialization support! Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
Sources/ParseSwift/Types/ParsePushPayload/ParsePushPayloadAny.swift (1)
15-38: YOUNG HERO! THE PIPELINE IS CRYING OUT FOR HELP!The build is failing because
ParsePushPayloadAnyno longer conforms toParsePushApplePayloadable! The protocol now requirescontentAvailableto be of typeInt?, but this struct declares it asAnyCodable?on line 37.This is a CRITICAL issue that must be resolved before this PR can be merged, PLUS ULTRA!
You have several options to fix this:
- Add a computed property that satisfies the protocol requirement while keeping the internal
AnyCodablefor decoding flexibility- Update the protocol conformance approach to handle the type mismatch
- Refactor to use
Int?directly and adjust the decoding logic💡 Potential approach using computed property
// Keep internal storage as AnyCodable for flexible decoding private var _contentAvailable: AnyCodable? // Satisfy protocol with computed property public var contentAvailable: Int? { get { _contentAvailable?.value as? Int } set { _contentAvailable = newValue.map { AnyCodable($0) } } }Sources/ParseSwift/Types/ParsePush.swift (1)
211-225: PLUS ULTRA: Fix the typo in the Boolean response error.
“resturn” → “return” to keep error text crisp.🛠️ Proposed fix
- throw ParseError(code: .otherCause, message: "The server did not resturn a Boolean response") + throw ParseError(code: .otherCause, message: "The server did not return a Boolean response")
🤖 Fix all issues with AI agents
In `@Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swift`:
- Around line 34-47: The new requirements (contentAvailable, mutableContent, and
init()) were added to the public protocol ParsePushApplePayloadable which is
source-breaking; to avoid breaking external conformers, remove these
requirements from ParsePushApplePayloadable and instead add them as stored
properties and an initializer on the concrete type ParsePushApplePayload (or
create a new opt-in protocol e.g., ParsePushApplePayloadWithFlags that declares
contentAvailable, mutableContent, and init()), and keep
ParsePushApplePayloadable unchanged so existing conformers continue to compile.
In `@Sources/ParseSwift/Types/ParsePushPayload/Apple/ParsePushPayloadApple.swift`:
- Around line 35-51: The documentation comment for the PushType enum exceeds the
120-character limit and several enum cases lack per-case docs; split the long
enum docstring into shorter lines (<=120 chars) and add brief documentation
comments for each case in enum PushType (e.g., alert, background, location,
voip, complication, fileprovider, mdm, pushtotalk, liveactivity) describing
their purpose so each case is documented and the header comment lines stay under
the limit.
In
`@Sources/ParseSwift/Types/ParsePushPayload/Apple/ParsePushPayloadAppleLiveActivity.swift`:
- Around line 55-91: The CodingKeys enum and the initializer init(from decoder:)
in ParsePushPayloadAppleLiveActivity are missing keys and decode paths for
event, priority, collapseId, pushType, and mdm; update the CodingKeys enum to
include case event, priority, collapseId = "collapse-id", pushType =
"push-type", and mdm, then in init(from decoder:) decode those fields (using
decodeIfPresent where appropriate) and set defaults: ensure priority defaults to
5 and pushType defaults to .liveactivity if nil; also add decoding for event,
collapseId and mdm to match their stored types so Live Activity fields are
serialized/deserialized correctly.
| /** | ||
| The background notification flag. If you are a writing an app using the Remote Notification | ||
| Background Mode introduced in iOS7 (a.k.a. “Background Push”), set this value to | ||
| 1 to trigger a background update. For more informaiton, see [Apple's documentation](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app). | ||
| - warning: For Apple OS's only. You also have to set `pushType` starting iOS 13 | ||
| and watchOS 6. | ||
| */ | ||
| var contentAvailable: Int? { get set } | ||
| /** | ||
| The notification service app extension flag. Set this value to 1 to trigger the system to pass the notification to your notification service app extension before delivery. Use your extension to modify the notification’s content. For more informaiton, see [Apple's documentation](https://developer.apple.com/documentation/usernotifications/modifying_content_in_newly_delivered_notifications). | ||
| - warning: You also have to set `pushType` starting iOS 13 | ||
| and watchOS 6. | ||
| */ | ||
| var mutableContent: Int? { get set } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "ParsePushApplePayloadable.swift" -type fRepository: netreconlab/Parse-Swift
Length of output: 129
🏁 Script executed:
cat -n ./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swiftRepository: netreconlab/Parse-Swift
Length of output: 6648
🏁 Script executed:
cd ./Sources/ParseSwift/Protocols && git diff HEAD~1 ParsePushApplePayloadable.swiftRepository: netreconlab/Parse-Swift
Length of output: 255
🏁 Script executed:
git log --oneline --max-count=20 -- ./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swiftRepository: netreconlab/Parse-Swift
Length of output: 112
🏁 Script executed:
git diff 24c9d60^..24c9d60 -- ./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swiftRepository: netreconlab/Parse-Swift
Length of output: 108
🏁 Script executed:
git show 24c9d60:./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swift | head -100Repository: netreconlab/Parse-Swift
Length of output: 4977
🏁 Script executed:
rg "ParsePushApplePayloadable" --type swift -A 3 | head -80Repository: netreconlab/Parse-Swift
Length of output: 2097
🏁 Script executed:
rg "ParsePushApplePayloadable" --type swift | grep -E "(struct|class).*ParsePushApplePayloadable"Repository: netreconlab/Parse-Swift
Length of output: 227
🏁 Script executed:
git log --all --oneline -- ./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swift | head -5Repository: netreconlab/Parse-Swift
Length of output: 237
🏁 Script executed:
git show 2410ff6:./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swift 2>/dev/null | grep -A 2 -E "(contentAvailable|mutableContent|^\s+init\(\))" | head -20Repository: netreconlab/Parse-Swift
Length of output: 49
🏁 Script executed:
git show 51a44ef:./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swift 2>/dev/null | head -100Repository: netreconlab/Parse-Swift
Length of output: 4058
🏁 Script executed:
git show 51a44ef:./Sources/ParseSwift/Protocols/ParsePushPayloadable.swift 2>/dev/null | head -50Repository: netreconlab/Parse-Swift
Length of output: 1081
🏁 Script executed:
git diff 51a44ef..2410ff6 -- ./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swiftRepository: netreconlab/Parse-Swift
Length of output: 598
🏁 Script executed:
git diff 51a44ef..HEAD -- ./Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swift | head -150Repository: netreconlab/Parse-Swift
Length of output: 4392
🏁 Script executed:
cat -n ./Sources/ParseSwift/Types/ParsePushPayload/ParsePushPayloadAny.swift | head -80Repository: netreconlab/Parse-Swift
Length of output: 3160
🏁 Script executed:
rg "ParsePushApplePayloadable\|ParsePushApplePayload" --type swift | grep -E ":\s*(ParsePushApplePayloadable|ParsePushApplePayload)"Repository: netreconlab/Parse-Swift
Length of output: 49
Source-breaking changes to public protocol require careful consideration!
Adding contentAvailable, mutableContent, and init() as new requirements to the public ParsePushApplePayloadable protocol breaks external conformers. This is a legitimate source-breaking change. If this isn't accompanied by a major-version bump, consider moving these requirements to ParsePushApplePayload instead, or introduce them as an opt-in protocol to maintain backward compatibility. I'm ready to help draft an alternative approach that keeps the protocol stable!
🤖 Prompt for AI Agents
In `@Sources/ParseSwift/Protocols/ParsePushApplePayloadable.swift` around lines 34
- 47, The new requirements (contentAvailable, mutableContent, and init()) were
added to the public protocol ParsePushApplePayloadable which is source-breaking;
to avoid breaking external conformers, remove these requirements from
ParsePushApplePayloadable and instead add them as stored properties and an
initializer on the concrete type ParsePushApplePayload (or create a new opt-in
protocol e.g., ParsePushApplePayloadWithFlags that declares contentAvailable,
mutableContent, and init()), and keep ParsePushApplePayloadable unchanged so
existing conformers continue to compile.
| /// The type of notification. | ||
| /// For more details, see [Apple Documentation](https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns). | ||
| public enum PushType: String, Codable, Sendable { | ||
| /// Send as an alert. | ||
| case alert | ||
| /// Send as a background notification. | ||
| case background | ||
| /// Send as a Push to Talk notification. | ||
| case location | ||
| case voip | ||
| case complication | ||
| case fileprovider | ||
| case mdm | ||
| case pushtotalk | ||
| /// Send as a Live Activity notification. | ||
| case liveactivity | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Fear not, young hero! But this documentation could use MORE POWER!
The static analysis has detected that Line 36 exceeds the 120 character limit (currently 151 characters)! Additionally, several enum cases lack documentation comments which would help fellow heroes understand their purpose!
📝 Proposed documentation and line length fix
/// The type of notification.
- /// For more details, see [Apple Documentation](https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns).
+ /// For more details, see Apple's documentation on
+ /// [sending notification requests to APNs](https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns).
public enum PushType: String, Codable, Sendable {
/// Send as an alert.
case alert
/// Send as a background notification.
case background
- /// Send as a Push to Talk notification.
+ /// Send as a location notification.
case location
+ /// Send as a VoIP notification.
case voip
+ /// Send as a watchOS complication notification.
case complication
+ /// Send as a file provider notification.
case fileprovider
+ /// Send as an MDM notification.
case mdm
+ /// Send as a Push to Talk notification.
case pushtotalk
/// Send as a Live Activity notification.
case liveactivity🧰 Tools
🪛 GitHub Check: Codacy Static Code Analysis
[notice] 36-36: Sources/ParseSwift/Types/ParsePushPayload/Apple/ParsePushPayloadApple.swift#L36
Line should be 120 characters or less; currently it has 151 characters
🤖 Prompt for AI Agents
In `@Sources/ParseSwift/Types/ParsePushPayload/Apple/ParsePushPayloadApple.swift`
around lines 35 - 51, The documentation comment for the PushType enum exceeds
the 120-character limit and several enum cases lack per-case docs; split the
long enum docstring into shorter lines (<=120 chars) and add brief documentation
comments for each case in enum PushType (e.g., alert, background, location,
voip, complication, fileprovider, mdm, pushtotalk, liveactivity) describing
their purpose so each case is documented and the header comment lines stay under
the limit.
| enum CodingKeys: String, CodingKey { | ||
| case relevanceScore = "relevance-score" | ||
| case targetContentId = "target-content-id" | ||
| case mutableContent = "mutable-content" | ||
| case contentAvailable = "content-available" | ||
| case interruptionLevel = "interruption-level" | ||
| case urlArgs = "url-args" | ||
| case threadId = "thread-id" | ||
| case category, sound, badge, alert, topic | ||
| } | ||
|
|
||
| public init() { | ||
| // Set to the lowest live activity priority by default. | ||
| priority = 5 | ||
| } | ||
|
|
||
| public init(from decoder: Decoder) throws { | ||
| let values = try decoder.container(keyedBy: CodingKeys.self) | ||
| do { | ||
| alert = try values.decode(ParsePushAppleAlert.self, forKey: .alert) | ||
| } catch { | ||
| if let alertBody = try values.decodeIfPresent(String.self, forKey: .alert) { | ||
| alert = ParsePushAppleAlert(body: alertBody) | ||
| } | ||
| } | ||
| relevanceScore = try values.decodeIfPresent(Double.self, forKey: .relevanceScore) | ||
| targetContentId = try values.decodeIfPresent(String.self, forKey: .targetContentId) | ||
| mutableContent = try values.decodeIfPresent(Int.self, forKey: .mutableContent) | ||
| contentAvailable = try values.decodeIfPresent(Int.self, forKey: .contentAvailable) | ||
| category = try values.decodeIfPresent(String.self, forKey: .category) | ||
| sound = try values.decodeIfPresent(AnyCodable.self, forKey: .sound) | ||
| badge = try values.decodeIfPresent(AnyCodable.self, forKey: .badge) | ||
| threadId = try values.decodeIfPresent(String.self, forKey: .threadId) | ||
| topic = try values.decodeIfPresent(String.self, forKey: .topic) | ||
| interruptionLevel = try values.decodeIfPresent(String.self, forKey: .interruptionLevel) | ||
| urlArgs = try values.decodeIfPresent([String].self, forKey: .urlArgs) | ||
| } |
There was a problem hiding this comment.
PLUS ULTRA: Live Activity fields are dropped from coding.
event, priority, collapseId, pushType, and mdm are missing from CodingKeys and decode logic, so they never serialize—risking broken Live Activity pushes. Add keys and decode them (keeping .liveactivity as the default).
🛠️ Proposed fix
enum CodingKeys: String, CodingKey {
+ case event
+ case priority
+ case collapseId = "collapse_id"
+ case pushType = "push_type"
+ case mdm = "_mdm"
case relevanceScore = "relevance-score"
case targetContentId = "target-content-id"
case mutableContent = "mutable-content"
case contentAvailable = "content-available"
case interruptionLevel = "interruption-level"
case urlArgs = "url-args"
case threadId = "thread-id"
case category, sound, badge, alert, topic
}
@@
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
do {
alert = try values.decode(ParsePushAppleAlert.self, forKey: .alert)
} catch {
if let alertBody = try values.decodeIfPresent(String.self, forKey: .alert) {
alert = ParsePushAppleAlert(body: alertBody)
}
}
+ event = try values.decodeIfPresent(Event.self, forKey: .event)
+ priority = try values.decodeIfPresent(Int.self, forKey: .priority)
+ collapseId = try values.decodeIfPresent(String.self, forKey: .collapseId)
+ pushType = try values.decodeIfPresent(ParsePushPayloadApple.PushType.self,
+ forKey: .pushType) ?? .liveactivity
+ mdm = try values.decodeIfPresent(String.self, forKey: .mdm)
relevanceScore = try values.decodeIfPresent(Double.self, forKey: .relevanceScore)
targetContentId = try values.decodeIfPresent(String.self, forKey: .targetContentId)
mutableContent = try values.decodeIfPresent(Int.self, forKey: .mutableContent)
contentAvailable = try values.decodeIfPresent(Int.self, forKey: .contentAvailable)
category = try values.decodeIfPresent(String.self, forKey: .category)
sound = try values.decodeIfPresent(AnyCodable.self, forKey: .sound)
badge = try values.decodeIfPresent(AnyCodable.self, forKey: .badge)
threadId = try values.decodeIfPresent(String.self, forKey: .threadId)
topic = try values.decodeIfPresent(String.self, forKey: .topic)
interruptionLevel = try values.decodeIfPresent(String.self, forKey: .interruptionLevel)
urlArgs = try values.decodeIfPresent([String].self, forKey: .urlArgs)
}🤖 Prompt for AI Agents
In
`@Sources/ParseSwift/Types/ParsePushPayload/Apple/ParsePushPayloadAppleLiveActivity.swift`
around lines 55 - 91, The CodingKeys enum and the initializer init(from
decoder:) in ParsePushPayloadAppleLiveActivity are missing keys and decode paths
for event, priority, collapseId, pushType, and mdm; update the CodingKeys enum
to include case event, priority, collapseId = "collapse-id", pushType =
"push-type", and mdm, then in init(from decoder:) decode those fields (using
decodeIfPresent where appropriate) and set defaults: ensure priority defaults to
5 and pushType defaults to .liveactivity if nil; also add decoding for event,
collapseId and mdm to match their stored types so Live Activity fields are
serialized/deserialized correctly.
New Pull Request Checklist
Issue Description
Approach
TODOs before merging