iOS/Sources/App/LifecycleManager.swift
Bruno Pantaleão Gonçalves e47b6a2662
Update todo list on app background + fix copy (#4350)
<!-- 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. -->
2026-02-13 12:05:36 +01:00

124 lines
4.0 KiB
Swift

import Foundation
import PromiseKit
import Shared
import UIKit
class LifecycleManager {
private let periodicUpdateManager = PeriodicUpdateManager(
applicationStateGetter: { UIApplication.shared.applicationState }
)
private var underlyingActive: UInt32 = 0
private(set) var isActive: Bool {
get {
OSAtomicOr32(0, &underlyingActive) != 0
}
set {
if newValue {
OSAtomicTestAndSet(0, &underlyingActive)
} else {
OSAtomicTestAndClear(0, &underlyingActive)
}
}
}
init() {
NotificationCenter.default.addObserver(
self,
selector: #selector(didBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
if Current.isCatalyst {
// on macOS, background/foreground is less of a concept
// on catalina, the app will 'background' and 'foreground' when hidden
// on big sur and beyond, the background/foreground lifecycle never seems to happen
NotificationCenter.default.addObserver(
self,
selector: #selector(warmConnect),
name: .init(rawValue: "NSApplicationDidBecomeActiveNotification"),
object: nil
)
} else {
NotificationCenter.default.addObserver(
self,
selector: #selector(warmConnect),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
}
NotificationCenter.default.addObserver(
self,
selector: #selector(willEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(didEnterBackground),
name: UIApplication.didEnterBackgroundNotification,
object: nil
)
}
func didFinishLaunching() {
Current.backgroundTask(withName: BackgroundTask.lifecycleManagerDidFinishLaunching.rawValue) { _ in
when(fulfilled: Current.apis.map { api in
api.CreateEvent(
eventType: "ios.finished_launching",
eventData: api.sharedEventDeviceInfo
)
})
}.cauterize()
periodicUpdateManager.connectAPI(reason: .cold)
}
@objc private func willEnterForeground() {
isActive = true
syncNetworkInformation()
}
@objc private func didEnterBackground() {
isActive = false
Current.backgroundTask(withName: BackgroundTask.lifecycleManagerDidEnterBackground.rawValue) { _ in
when(fulfilled: Current.apis.map { api in
api.CreateEvent(
eventType: "ios.entered_background",
eventData: api.sharedEventDeviceInfo
)
})
}.cauterize()
periodicUpdateManager.invalidatePeriodicUpdateTimer(forBackground: true)
DataWidgetsUpdater.update()
}
private var hasTriggeredWarm = false
@objc private func warmConnect() {
if hasTriggeredWarm {
// iOS 13+ scene API triggers foreground on initial launch, too, so we ignore it
periodicUpdateManager.connectAPI(reason: .warm)
}
hasTriggeredWarm = true
}
@objc private func didBecomeActive() {
Current.backgroundTask(withName: BackgroundTask.lifecycleManagerDidBecomeActive.rawValue) { _ in
when(fulfilled: Current.apis.map { api in
api.CreateEvent(
eventType: "ios.became_active",
eventData: api.sharedEventDeviceInfo
)
})
}.cauterize()
syncNetworkInformation()
}
private func syncNetworkInformation() {
Task {
await Current.connectivity.syncNetworkInformation()
}
}
}