mirror of
https://github.com/home-assistant/iOS.git
synced 2026-02-04 02:46:35 -06:00
<!-- Thank you for submitting a Pull Request and helping to improve Home Assistant. Please complete the following sections to help the processing and review of your changes. Please do not delete anything from this template. --> ## Summary <!-- Provide a brief summary of the changes you have made and most importantly what they aim to achieve --> ## Screenshots <!-- If this is a user-facing change not in the frontend, please include screenshots in light and dark mode. --> ## Link to pull request in Documentation repository <!-- Pull requests that add, change or remove functionality must have a corresponding pull request in the Companion App Documentation repository (https://github.com/home-assistant/companion.home-assistant). Please add the number of this pull request after the "#" --> Documentation: home-assistant/companion.home-assistant# ## Any other notes <!-- If there is any other information of note, like if this Pull Request is part of a bigger change, please include it here. --> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
107 lines
3.8 KiB
Swift
107 lines
3.8 KiB
Swift
import Foundation
|
|
import PromiseKit
|
|
#if os(iOS)
|
|
import UIKit
|
|
#endif
|
|
|
|
public enum BackgroundTask: String {
|
|
case backgroundFetch = "background-fetch"
|
|
case lifecycleManagerDidFinishLaunching = "lifecycle-manager-didFinishLaunching"
|
|
case lifecycleManagerDidEnterBackground = "lifecycle-manager-didEnterBackground"
|
|
case lifecycleManagerDidBecomeActive = "lifecycle-manager-didBecomeActive"
|
|
case shortcutItem = "shortcut-item"
|
|
case handlePushAction = "handle-push-action"
|
|
case notificationManagerDidReceiveRegistrationToken = "notificationManager-didReceiveRegistrationToken"
|
|
case zoneManagerPerformEvent = "zone-manager-perform-event"
|
|
case watchPushAction = "watch-push-action"
|
|
case webhookSendEphemeral = "webhook-send-ephemeral"
|
|
case webhookSend = "webhook-send"
|
|
case webhookInvoke = "webhook-invoke"
|
|
case manualLocationUpdate = "manual-location-update"
|
|
case signaledUpdateSensors = "signaled-update-sensors"
|
|
case connectApi = "connect-api"
|
|
case realmWrite = "realm-write"
|
|
case pushLocationRequest = "push-location-request"
|
|
}
|
|
|
|
public enum BackgroundTaskError: Error {
|
|
case outOfTime
|
|
}
|
|
|
|
public protocol HomeAssistantBackgroundTaskRunner {
|
|
func callAsFunction<PromiseValue>(
|
|
withName name: String,
|
|
wrapping: (TimeInterval?) -> Promise<PromiseValue>
|
|
) -> Promise<PromiseValue>
|
|
}
|
|
|
|
// enum for namespacing
|
|
public enum HomeAssistantBackgroundTask {
|
|
public static func execute<ReturnType, IdentifierType>(
|
|
withName name: String,
|
|
beginBackgroundTask: (String, @escaping () -> Void) -> (IdentifierType?, TimeInterval?),
|
|
endBackgroundTask: @escaping (IdentifierType) -> Void,
|
|
wrapping: (TimeInterval?) -> Promise<ReturnType>
|
|
) -> Promise<ReturnType> {
|
|
func describe(_ identifier: IdentifierType?) -> String {
|
|
if let identifier {
|
|
#if os(iOS)
|
|
if let identifier = identifier as? UIBackgroundTaskIdentifier {
|
|
return String(describing: identifier.rawValue)
|
|
} else {
|
|
return String(describing: identifier)
|
|
}
|
|
#else
|
|
return String(describing: identifier)
|
|
#endif
|
|
} else {
|
|
return "(none)"
|
|
}
|
|
}
|
|
|
|
var identifier: IdentifierType?
|
|
var remaining: TimeInterval?
|
|
|
|
// we can't guarantee to Swift that this will be assigned, but it will
|
|
var finished: () -> Void = {}
|
|
|
|
let promise = Promise<Void> { seal in
|
|
(identifier, remaining) = beginBackgroundTask(name) {
|
|
Current.Log.error("out of time for background task \(name) \(describe(identifier))")
|
|
seal.reject(BackgroundTaskError.outOfTime)
|
|
}
|
|
|
|
finished = {
|
|
seal.fulfill(())
|
|
}
|
|
}.tap { result in
|
|
guard let endableIdentifier = identifier else { return }
|
|
|
|
let endBackgroundTask = {
|
|
endBackgroundTask(endableIdentifier)
|
|
identifier = nil
|
|
}
|
|
|
|
if case .rejected(BackgroundTaskError.outOfTime) = result {
|
|
// immediately execute, or we'll be terminated by the system!
|
|
endBackgroundTask()
|
|
} else {
|
|
// give it a run loop, since we want the promise's e.g. completion handlers to be invoked first
|
|
DispatchQueue.main.async { endBackgroundTask() }
|
|
}
|
|
}
|
|
|
|
// make sure we only invoke the promise-returning block once, in case it has side-effects
|
|
let underlying = wrapping(remaining)
|
|
|
|
let underlyingWithFinished = underlying
|
|
.ensure { finished() }
|
|
|
|
return firstly {
|
|
when(fulfilled: [promise.asVoid(), underlyingWithFinished.asVoid()])
|
|
}.then {
|
|
underlying
|
|
}
|
|
}
|
|
}
|