feat: implement Live Activity registration service and enhance notifications handling
This commit is contained in:
@@ -14,9 +14,61 @@ struct AlertAttributes: ActivityAttributes {
|
||||
let updatedAt: Date
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case title, value, status
|
||||
case startedAt = "startedAt"
|
||||
case updatedAt = "updatedAt"
|
||||
case title, value, status, startedAt, updatedAt
|
||||
}
|
||||
|
||||
init(title: String, value: String?, status: AlertStatus, startedAt: Date, updatedAt: Date) {
|
||||
self.title = title
|
||||
self.value = value
|
||||
self.status = status
|
||||
self.startedAt = startedAt
|
||||
self.updatedAt = updatedAt
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||
title = try c.decode(String.self, forKey: .title)
|
||||
value = try c.decodeIfPresent(String.self, forKey: .value)
|
||||
status = try c.decode(AlertStatus.self, forKey: .status)
|
||||
startedAt = try Self.decodeDate(from: c, forKey: .startedAt)
|
||||
updatedAt = try Self.decodeDate(from: c, forKey: .updatedAt)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var c = encoder.container(keyedBy: CodingKeys.self)
|
||||
try c.encode(title, forKey: .title)
|
||||
try c.encodeIfPresent(value, forKey: .value)
|
||||
try c.encode(status, forKey: .status)
|
||||
try c.encode(Self.isoFormatter.string(from: startedAt), forKey: .startedAt)
|
||||
try c.encode(Self.isoFormatter.string(from: updatedAt), forKey: .updatedAt)
|
||||
}
|
||||
|
||||
// ActivityKit uses the default JSONDecoder when materialising ContentState
|
||||
// from a remote push payload, so dates round-trip as ISO8601 strings
|
||||
// matching the backend's time.RFC3339[Nano] output. Both formatters are
|
||||
// pre-built and treated as immutable; ISO8601DateFormatter is documented
|
||||
// thread-safe for read use.
|
||||
nonisolated(unsafe) private static let isoWithFractional: ISO8601DateFormatter = {
|
||||
let f = ISO8601DateFormatter()
|
||||
f.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
||||
return f
|
||||
}()
|
||||
nonisolated(unsafe) private static let isoWithoutFractional: ISO8601DateFormatter = {
|
||||
let f = ISO8601DateFormatter()
|
||||
f.formatOptions = [.withInternetDateTime]
|
||||
return f
|
||||
}()
|
||||
// Single shared encoder format: ISO8601 without fractional seconds,
|
||||
// since iOS reconstructs Date from string and sub-second precision is
|
||||
// irrelevant for the alert timestamps we surface.
|
||||
private static var isoFormatter: ISO8601DateFormatter { isoWithoutFractional }
|
||||
|
||||
private static func decodeDate(from c: KeyedDecodingContainer<CodingKeys>, forKey key: CodingKeys) throws -> Date {
|
||||
let s = try c.decode(String.self, forKey: key)
|
||||
if let d = isoWithFractional.date(from: s) ?? isoWithoutFractional.date(from: s) {
|
||||
return d
|
||||
}
|
||||
throw DecodingError.dataCorruptedError(forKey: key, in: c, debugDescription: "Invalid ISO8601 date: \(s)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user