Files
iOS/Sources/Shared/API/Webhook/Networking/WebhookResponseLocation.swift
Zac West 7cbde6ddb2 Try and fix Realm crashes via excessive background tasks (#1874)
Starting in iOS 15, there's a number of crashes happening in the background with Realm. They don't appear to be due to the file lock in the shared app container, but this may help resolve them either way -- easy to see if the next beta doesn't crash a bunch.
2021-10-03 21:28:00 +00:00

99 lines
2.9 KiB
Swift

import Foundation
import ObjectMapper
import PromiseKit
import UserNotifications
extension WebhookResponseIdentifier {
static var location: Self { .init(rawValue: "updateLocation") }
}
struct WebhookResponseLocationLocalMetadata: ImmutableMappable {
let trigger: LocationUpdateTrigger
let zoneName: String
init(trigger: LocationUpdateTrigger, zone: RLMZone?) {
self.trigger = trigger
self.zoneName = zone?.Name ?? "(unknown)"
}
init(map: Map) throws {
self.trigger = try map.value("trigger")
self.zoneName = try map.value("zone_name")
}
func mapping(map: Map) {
trigger >>> map["trigger"]
zoneName >>> map["zone_name"]
}
}
struct WebhookResponseLocation: WebhookResponseHandler {
let api: HomeAssistantAPI
init(api: HomeAssistantAPI) {
self.api = api
}
static func localMetdata(
trigger: LocationUpdateTrigger,
zone: RLMZone?
) -> [String: Any] {
WebhookResponseLocationLocalMetadata(
trigger: trigger,
zone: zone
).toJSON()
}
static func shouldReplace(request current: WebhookRequest, with proposed: WebhookRequest) -> Bool {
// recency should always win
true
}
enum HandleError: Error {
case missingLocalMetadata
}
func handle(
request: Promise<WebhookRequest>,
result: Promise<Any>
) -> Guarantee<WebhookResponseHandlerResult> {
firstly {
when(fulfilled: request, result)
}.map { request, _ in
guard let localMetadata = request.localMetadata.flatMap({
Mapper<WebhookResponseLocationLocalMetadata>().map(JSON: $0)
}) else {
throw HandleError.missingLocalMetadata
}
let notificationOptions = localMetadata.trigger.notificationOptionsFor(zoneName: localMetadata.zoneName)
Current.clientEventStore.addEvent(ClientEvent(
text: notificationOptions.body,
type: .locationUpdate,
payload: try? request.asDictionary()
)).cauterize()
guard notificationOptions.shouldNotify else {
return nil
}
return UNNotificationRequest(
identifier: notificationOptions.identifier ?? UUID().uuidString,
content: with(UNMutableNotificationContent()) {
$0.title = notificationOptions.title
$0.body = notificationOptions.body
$0.sound = UNNotificationSound.default
},
trigger: nil
)
}.recover { _ -> Guarantee<UNNotificationRequest?> in
// don't send a notification for failed
.value(nil)
}.map { notification in
var result = WebhookResponseHandlerResult.default
result.notification = notification
return result
}
}
}