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 | } | |