Commit 81e97f81a5
Verified · cmc
Layout: unified · split
gitbay/Push/PushPayload.swift added +28
| @@ -0,0 +1,28 @@ | |||
| 1 | import Foundation | ||
| 2 | |||
| 3 | /// A push notification as the app reads it. | ||
| 4 | /// | ||
| 5 | /// `instance` and `user` name the account the notice belongs to. One | ||
| 6 | /// device token is one install, and an install registers against every | ||
| 7 | /// account signed in on it, so `path` alone is ambiguous — two | ||
| 8 | /// instances can hold the same `owner/name`. | ||
| 9 | nonisolated struct PushPayload: Equatable, Sendable { | ||
| 10 | let instance: URL | ||
| 11 | let user: String | ||
| 12 | let path: String | ||
| 13 | /// The recipient's unread count, for the app icon badge. Absent on | ||
| 14 | /// a notification from a server that predates it. | ||
| 15 | let badge: Int | ||
| 16 | |||
| 17 | init?(userInfo: [AnyHashable: Any]) { | ||
| 18 | guard let raw = userInfo["instance"] as? String, | ||
| 19 | let instance = URL(string: raw), instance.scheme != nil, | ||
| 20 | let user = userInfo["user"] as? String, !user.isEmpty, | ||
| 21 | let path = userInfo["path"] as? String, !path.isEmpty | ||
| 22 | else { return nil } | ||
| 23 | self.instance = instance | ||
| 24 | self.user = user | ||
| 25 | self.path = path | ||
| 26 | self.badge = (userInfo["aps"] as? [AnyHashable: Any])?["badge"] as? Int ?? 0 | ||
| 27 | } | ||
| 28 | } | ||
gitbayTests/PushPayloadTests.swift added +34
| @@ -0,0 +1,34 @@ | |||
| 1 | import Foundation | ||
| 2 | import Testing | ||
| 3 | @testable import gitbay | ||
| 4 | |||
| 5 | @Test func payloadDecodesTheAccountAndPath() throws { | ||
| 6 | let userInfo: [AnyHashable: Any] = [ | ||
| 7 | "aps": ["alert": ["title": "krz/gitbay", "body": "cmc opened issue #12"], "badge": 3], | ||
| 8 | "instance": "https://gitbay.org", | ||
| 9 | "user": "cmc", | ||
| 10 | "path": "krz/gitbay/issues/12", | ||
| 11 | ] | ||
| 12 | let payload = try #require(PushPayload(userInfo: userInfo)) | ||
| 13 | #expect(payload.instance == URL(string: "https://gitbay.org")) | ||
| 14 | #expect(payload.user == "cmc") | ||
| 15 | #expect(payload.path == "krz/gitbay/issues/12") | ||
| 16 | #expect(payload.badge == 3) | ||
| 17 | } | ||
| 18 | |||
| 19 | @Test func payloadTreatsAMissingBadgeAsZero() throws { | ||
| 20 | let userInfo: [AnyHashable: Any] = [ | ||
| 21 | "aps": ["alert": ["title": "t", "body": "b"]], | ||
| 22 | "instance": "https://gitbay.org", "user": "cmc", "path": "a/b/issues/1", | ||
| 23 | ] | ||
| 24 | let payload = try #require(PushPayload(userInfo: userInfo)) | ||
| 25 | #expect(payload.badge == 0) | ||
| 26 | } | ||
| 27 | |||
| 28 | // A payload from a server that predates the account fields, or one | ||
| 29 | // mangled in transit, is dropped rather than guessed at. | ||
| 30 | @Test func payloadRejectsAnIncompleteNotification() { | ||
| 31 | #expect(PushPayload(userInfo: ["aps": ["alert": "x"], "path": "a/b/issues/1"]) == nil) | ||
| 32 | #expect(PushPayload(userInfo: ["instance": "https://gitbay.org", "user": "cmc"]) == nil) | ||
| 33 | #expect(PushPayload(userInfo: ["instance": "not a url at all", "user": "cmc", "path": "a/b/issues/1"]) == nil) | ||
| 34 | } | ||