PushNotificationDataObjects.swift 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import Foundation
  2. public struct PushNotification: Codable, Equatable {
  3. public let accessToken: String
  4. public let preferredLocale: String
  5. public let notificationId: Int64
  6. public let notificationType: Type
  7. public let icon: URL
  8. public let title: String
  9. public let body: String
  10. enum CodingKeys: String, CodingKey {
  11. case accessToken = "access_token"
  12. case preferredLocale = "preferred_locale"
  13. case notificationId = "notification_id"
  14. case notificationType = "notification_type"
  15. case icon = "icon"
  16. case title = "title"
  17. case body = "body"
  18. }
  19. public enum `Type`: String, Codable, Equatable {
  20. case favourite = "favourite"
  21. case follow = "follow"
  22. case mention = "mention"
  23. case reblog = "reblog"
  24. }
  25. }
  26. public struct PushNotificationSubscription: Codable, Equatable {
  27. public let endpoint: URL
  28. public let alerts: PushNotificationAlerts
  29. public init(
  30. endpoint: URL,
  31. alerts: PushNotificationAlerts
  32. ) {
  33. self.endpoint = endpoint
  34. self.alerts = alerts
  35. }
  36. }
  37. public struct PushNotificationAlerts: Codable, Equatable {
  38. public let favourite: Bool
  39. public let follow: Bool
  40. public let mention: Bool
  41. public let reblog: Bool
  42. public static var all = PushNotificationAlerts(favourite: true, follow: true, mention: true, reblog: true)
  43. public init(
  44. favourite: Bool,
  45. follow: Bool,
  46. mention: Bool,
  47. reblog: Bool
  48. ) {
  49. self.favourite = favourite
  50. self.follow = follow
  51. self.mention = mention
  52. self.reblog = reblog
  53. }
  54. public var isActive: Bool {
  55. return favourite || follow || mention || reblog
  56. }
  57. }
  58. public struct PushNotificationSubscriptionRequest: Codable, Equatable {
  59. public let subscription: Subscription?
  60. public let data: Data
  61. public init(
  62. subscription: Subscription?,
  63. data: Data
  64. ) {
  65. self.subscription = subscription
  66. self.data = data
  67. }
  68. public struct Subscription: Codable, Equatable {
  69. public let endpoint: String
  70. public let keys: Keys
  71. public init(
  72. endpoint: String,
  73. keys: Keys
  74. ) {
  75. self.endpoint = endpoint
  76. self.keys = keys
  77. }
  78. public struct Keys: Codable, Equatable {
  79. public let p256dh: String
  80. public let auth: String
  81. public init(
  82. p256dh: String,
  83. auth: String
  84. ) {
  85. self.p256dh = p256dh
  86. self.auth = auth
  87. }
  88. }
  89. }
  90. public struct Data: Codable, Equatable {
  91. public let alerts: PushNotificationAlerts
  92. public init(alerts: PushNotificationAlerts) {
  93. self.alerts = alerts
  94. }
  95. }
  96. }
  97. extension PushNotificationSubscriptionRequest {
  98. public init(endpoint: String, receiver: PushNotificationReceiver, alerts: PushNotificationAlerts) {
  99. self.init(
  100. subscription: .init(
  101. endpoint: endpoint,
  102. keys: .init(
  103. p256dh: receiver.publicKeyData.base64UrlEncodedString(),
  104. auth: receiver.authentication.base64UrlEncodedString()
  105. )
  106. ), data: .init(alerts: alerts)
  107. )
  108. }
  109. }
  110. extension Data {
  111. func base64UrlEncodedString() -> String {
  112. return base64EncodedString()
  113. .replacingOccurrences(of: "+", with: "-")
  114. .replacingOccurrences(of: "/", with: "_")
  115. .replacingOccurrences(of: "=", with: "")
  116. }
  117. }
  118. public struct PushNotificationDeviceToken: Codable, Equatable {
  119. public let deviceToken: Data
  120. public let isProduction: Bool
  121. public init(deviceToken: Data) {
  122. self.deviceToken = deviceToken
  123. let startData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>".data(using: .ascii)!
  124. let endData = "</plist>".data(using: .ascii)!
  125. if let url = Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision"),
  126. let data = try? Data(contentsOf: url),
  127. let startIndex = data.range(of: startData)?.lowerBound,
  128. let endIndex = data.range(of: endData)?.upperBound,
  129. let plist = try? PropertyListSerialization.propertyList(from: data[startIndex ..< endIndex], options: [], format: nil),
  130. let dict = plist as? [String: Any],
  131. let entitlements = dict["Entitlements"] as? [String: Any],
  132. entitlements["aps-environment"] as? String == "development" {
  133. self.isProduction = false
  134. } else {
  135. self.isProduction = true
  136. }
  137. }
  138. public func endpoint(service: URL, extra: String?) -> URL {
  139. var endpoint = service
  140. endpoint.appendPathComponent(isProduction ? "production" : "development")
  141. endpoint.appendPathComponent(deviceToken.hexString)
  142. if let extra = extra {
  143. endpoint.appendPathComponent(extra)
  144. }
  145. return endpoint
  146. }
  147. }
  148. extension Data {
  149. var hexString: String {
  150. return map { String(format: "%02x", $0) }.joined()
  151. }
  152. func range(of substring: Data) -> Range<Int>? {
  153. for i in 0 ..< count - substring.count {
  154. var match = true
  155. for j in 0 ..< substring.count {
  156. if self[i + j] != substring[j] {
  157. match = false
  158. break
  159. }
  160. }
  161. if match {
  162. return i ..< i + substring.count
  163. }
  164. }
  165. return nil
  166. }
  167. }
  168. public struct PushNotificationState: Codable, Equatable {
  169. public let receiver: PushNotificationReceiver
  170. public let subscription: PushNotificationSubscription
  171. public let deviceToken: PushNotificationDeviceToken
  172. public init(
  173. receiver: PushNotificationReceiver,
  174. subscription: PushNotificationSubscription,
  175. deviceToken: PushNotificationDeviceToken
  176. ) {
  177. self.receiver = receiver
  178. self.subscription = subscription
  179. self.deviceToken = deviceToken
  180. }
  181. }
  182. extension PushNotificationState {
  183. public func with(subscription: PushNotificationSubscription) -> PushNotificationState {
  184. return .init(
  185. receiver: receiver,
  186. subscription: subscription,
  187. deviceToken: deviceToken
  188. )
  189. }
  190. public func with(alerts: PushNotificationAlerts) -> PushNotificationState {
  191. return .init(
  192. receiver: receiver,
  193. subscription: .init(
  194. endpoint: subscription.endpoint,
  195. alerts: alerts
  196. ),
  197. deviceToken: deviceToken
  198. )
  199. }
  200. }