mirror of
https://github.com/home-assistant/iOS.git
synced 2026-06-19 07:24:05 -05: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 --> There is a long term bug in iOS which calls widget timelines several times even though once would be enough to render the widget. Since apparently Apple is not giving attention to that and out custom widgets make data calls, this PRs ads a small cache to prevent unecessary data calls. ## 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. -->
168 lines
6.1 KiB
Swift
168 lines
6.1 KiB
Swift
import Foundation
|
|
import PromiseKit
|
|
import Shared
|
|
import UIKit
|
|
import WidgetKit
|
|
|
|
final class WebViewSceneDelegate: NSObject, UIWindowSceneDelegate {
|
|
var window: UIWindow?
|
|
var windowController: WebViewWindowController?
|
|
var urlHandler: IncomingURLHandler?
|
|
|
|
// swiftlint:disable cyclomatic_complexity
|
|
func scene(
|
|
_ scene: UIScene,
|
|
willConnectTo session: UISceneSession,
|
|
options connectionOptions: UIScene.ConnectionOptions
|
|
) {
|
|
guard let scene = scene as? UIWindowScene else { return }
|
|
// if it tries to connect for an external display, decline -- it'll mirror instead
|
|
guard session.role != .windowExternalDisplay else { return }
|
|
|
|
ScaleFactorMutator.record(sceneIdentifier: session.persistentIdentifier)
|
|
|
|
let window = UIWindow(haScene: scene)
|
|
let windowController = WebViewWindowController(
|
|
window: window,
|
|
restorationActivity: session.stateRestorationActivity
|
|
)
|
|
let urlHandler = IncomingURLHandler(windowController: windowController)
|
|
self.window = window
|
|
self.windowController = windowController
|
|
self.urlHandler = urlHandler
|
|
|
|
with(scene.sizeRestrictions) {
|
|
if scene.traitCollection.userInterfaceIdiom == .mac {
|
|
$0?.minimumSize = CGSize(width: 250, height: 250)
|
|
} else {
|
|
$0?.minimumSize = CGSize(width: 300, height: 300)
|
|
}
|
|
}
|
|
|
|
if Current.isCatalyst, Current.settingsStore.macNativeFeaturesOnly {
|
|
// This getter does not exist on macOS 10.15, so we need to check that it responds.
|
|
// Of course, this is not documented via availability headers, of course.
|
|
if connectionOptions.responds(to: #selector(getter: UIScene.ConnectionOptions.shortcutItem)),
|
|
let shortcutItem = connectionOptions.shortcutItem {
|
|
self.windowScene(scene, performActionFor: shortcutItem, completionHandler: { _ in })
|
|
} else if let url = Current.servers.all.first?.info.connection.activeURL() {
|
|
UIApplication.shared.open(url)
|
|
// Close window to avoid empty window left behind
|
|
if let scene = window.windowScene {
|
|
UIApplication.shared.requestSceneSessionDestruction(scene.session, options: nil, errorHandler: nil)
|
|
}
|
|
}
|
|
} else {
|
|
windowController.setup()
|
|
|
|
// This getter does not exist on macOS 10.15, so we need to check that it responds.
|
|
// Of course, this is not documented via availability headers, of course.
|
|
if connectionOptions.responds(to: #selector(getter: UIScene.ConnectionOptions.shortcutItem)),
|
|
let shortcutItem = connectionOptions.shortcutItem {
|
|
self.windowScene(scene, performActionFor: shortcutItem, completionHandler: { _ in })
|
|
}
|
|
}
|
|
#if targetEnvironment(macCatalyst)
|
|
if let titlebar = scene.titlebar {
|
|
// disabling this also disables the "show tab bar" window tab bar (aka not uitabbar)
|
|
titlebar.titleVisibility = .hidden
|
|
titlebar.toolbar = nil
|
|
}
|
|
#endif
|
|
|
|
if !connectionOptions.urlContexts.isEmpty {
|
|
self.scene(scene, openURLContexts: connectionOptions.urlContexts)
|
|
}
|
|
|
|
if !connectionOptions.userActivities.isEmpty {
|
|
for activity in connectionOptions.userActivities {
|
|
self.scene(scene, continue: activity)
|
|
}
|
|
}
|
|
|
|
informManager(from: connectionOptions)
|
|
|
|
#if targetEnvironment(macCatalyst)
|
|
WindowScenesManager.shared.sceneDidBecomeActive(scene)
|
|
#endif
|
|
}
|
|
|
|
func sceneWillResignActive(_ scene: UIScene) {
|
|
#if targetEnvironment(macCatalyst)
|
|
WindowScenesManager.shared.sceneWillResignActive(scene)
|
|
#endif
|
|
|
|
DataWidgetsUpdater.update()
|
|
}
|
|
|
|
func sceneDidDisconnect(_ scene: UIScene) {
|
|
windowController?.clearCachedControllers()
|
|
windowController = nil
|
|
window = nil
|
|
urlHandler = nil
|
|
|
|
#if targetEnvironment(macCatalyst)
|
|
WindowScenesManager.shared.didDiscardScene(scene)
|
|
#endif
|
|
|
|
DataWidgetsUpdater.update()
|
|
}
|
|
|
|
func sceneDidEnterBackground(_ scene: UIScene) {
|
|
if #available(iOS 17.0, *) {
|
|
// if a widget is pending confirmation to execute it's action
|
|
// this will reset that and the widget will be restored to default state
|
|
_ = ResetAllCustomWidgetConfirmationAppIntent()
|
|
}
|
|
DataWidgetsUpdater.update()
|
|
Current.modelManager.unsubscribe()
|
|
Current.appDatabaseUpdater.stop()
|
|
}
|
|
|
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
|
DataWidgetsUpdater.update()
|
|
Current.modelManager.cleanup().cauterize()
|
|
Current.modelManager.subscribe(isAppInForeground: {
|
|
UIApplication.shared.applicationState == .active
|
|
})
|
|
Current.appDatabaseUpdater.update()
|
|
Current.panelsUpdater.update()
|
|
|
|
let widgetsCacheFile = AppConstants.widgetsCacheURL
|
|
|
|
// Clean up widgets cache file
|
|
do {
|
|
try FileManager.default.removeItem(at: widgetsCacheFile)
|
|
} catch {
|
|
Current.Log.error("Failed to remove widgets cache file: \(error)")
|
|
}
|
|
}
|
|
|
|
func windowScene(
|
|
_ windowScene: UIWindowScene,
|
|
performActionFor shortcutItem: UIApplicationShortcutItem,
|
|
completionHandler: @escaping (Bool) -> Void
|
|
) {
|
|
urlHandler?.handle(shortcutItem: shortcutItem)
|
|
.done {
|
|
completionHandler(true)
|
|
}.catch { _ in
|
|
completionHandler(false)
|
|
}
|
|
}
|
|
|
|
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
|
for url in URLContexts.map(\.url) {
|
|
urlHandler?.handle(url: url)
|
|
}
|
|
}
|
|
|
|
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
|
|
windowController?.stateRestorationActivity()
|
|
}
|
|
|
|
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
|
|
urlHandler?.handle(userActivity: userActivity)
|
|
}
|
|
}
|