Skip to content

Commit

Permalink
Keep only necessary webviews alive in the app (#2749)
Browse files Browse the repository at this point in the history
  • Loading branch information
bgoncal committed May 6, 2024
1 parent 674cd54 commit 9a83285
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 19 deletions.
4 changes: 4 additions & 0 deletions HomeAssistant.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@
42B94BDF2B9606CD00DEE060 /* AssistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B94BDC2B9606CD00DEE060 /* AssistView.swift */; };
42B94BEC2B96083C00DEE060 /* AssistModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B94BE72B9607D100DEE060 /* AssistModel.swift */; };
42B94BED2B96083C00DEE060 /* AssistModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B94BE72B9607D100DEE060 /* AssistModel.swift */; };
42B95B522BE007E30070F2D4 /* SafeScriptMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B95B512BE007E30070F2D4 /* SafeScriptMessageHandler.swift */; };
42C08CF72BA31F2700172EE5 /* CMSampleBuffer+AudioSamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C08CF62BA31F2700172EE5 /* CMSampleBuffer+AudioSamples.swift */; };
42C3737F2BC415AC00898990 /* UIViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C3737E2BC415AC00898990 /* UIViewController+Extensions.swift */; };
42C373B22BC5382900898990 /* HostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C373B12BC5382900898990 /* HostingController.swift */; };
Expand Down Expand Up @@ -1750,6 +1751,7 @@
42B94BDB2B9606CD00DEE060 /* AssistViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssistViewModel.swift; sourceTree = "<group>"; };
42B94BDC2B9606CD00DEE060 /* AssistView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssistView.swift; sourceTree = "<group>"; };
42B94BE72B9607D100DEE060 /* AssistModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AssistModel.swift; path = Sources/Shared/Intents/AssistInApp/AssistModel.swift; sourceTree = SOURCE_ROOT; };
42B95B512BE007E30070F2D4 /* SafeScriptMessageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafeScriptMessageHandler.swift; sourceTree = "<group>"; };
42C08CF62BA31F2700172EE5 /* CMSampleBuffer+AudioSamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CMSampleBuffer+AudioSamples.swift"; sourceTree = "<group>"; };
42C3737E2BC415AC00898990 /* UIViewController+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extensions.swift"; sourceTree = "<group>"; };
42C373AF2BC536AA00898990 /* WatchApp-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WatchApp-Bridging-Header.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2791,6 +2793,7 @@
113FB1122515A065000AC680 /* ScaleFactorMutator.swift */,
11DE822D24FAC51000E636B8 /* IncomingURLHandler.swift */,
B64BB3A71E9C6551001E8B46 /* WebViewController.swift */,
42B95B512BE007E30070F2D4 /* SafeScriptMessageHandler.swift */,
11EFCDD224F5F39100314D85 /* WebViewWindowController.swift */,
42DD84142B14D68C00936F16 /* WebViewExternalBusMessage.swift */,
);
Expand Down Expand Up @@ -5957,6 +5960,7 @@
B648AE262275918F006972AF /* Scenes.swift in Sources */,
1185DF9A271FE60F00ED7D9A /* OnboardingAuthStep.swift in Sources */,
420FE8502B556F7500878E06 /* CarPlayEntitiesListTemplate+Build.swift in Sources */,
42B95B522BE007E30070F2D4 /* SafeScriptMessageHandler.swift in Sources */,
11F55EBC25D3A2A3003977AC /* NotificationCategoryListViewController.swift in Sources */,
1100D51F2496F63400B1073C /* ThemeColors.swift in Sources */,
1185DFB4271FF53800ED7D9A /* OnboardingAuthStepModels.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Sources/App/Scenes/WebViewSceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ final class WebViewSceneDelegate: NSObject, UIWindowSceneDelegate {
}

func sceneDidDisconnect(_ scene: UIScene) {
windowController?.clearCachedControllers()
windowController = nil
window = nil
urlHandler = nil
Expand Down
11 changes: 6 additions & 5 deletions Sources/App/WebView/IncomingURLHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import SafariServices
import Shared

class IncomingURLHandler {
let windowController: WebViewWindowController
private(set) weak var windowController: WebViewWindowController!

init(windowController: WebViewWindowController) {
self.windowController = windowController
registerCallbackURLKitHandlers()
Expand Down Expand Up @@ -62,7 +63,7 @@ class IncomingURLHandler {
presenting is SFSafariViewController {
// Dismiss my.* controller if it's on top - we don't get any other indication
presenting.dismiss(animated: true, completion: { [windowController] in
windowController.openSelectingServer(
windowController?.openSelectingServer(
from: .deeplink,
urlString: rawURL,
skipConfirm: true,
Expand Down Expand Up @@ -220,7 +221,7 @@ class IncomingURLHandler {
}
))

windowController.webViewControllerPromise.done {
windowController?.webViewControllerPromise.done {
$0.present(alert, animated: true, completion: nil)
}
}
Expand All @@ -232,7 +233,7 @@ class IncomingURLHandler {
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: L10n.okLabel, style: .default, handler: nil))
windowController.webViewControllerPromise.done {
windowController?.webViewControllerPromise.done {
$0.present(alert, animated: true, completion: nil)
}
}
Expand All @@ -252,7 +253,7 @@ class IncomingURLHandler {
}

// not animated in because it looks weird during the app launch animation
windowController.present(SFSafariViewController(url: updatedURL), animated: false, completion: nil)
windowController?.present(SFSafariViewController(url: updatedURL), animated: false, completion: nil)

return true
}
Expand Down
20 changes: 20 additions & 0 deletions Sources/App/WebView/SafeScriptMessageHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation
import WebKit

/// Use to avoid holding webview alive when adding WKScriptMessageHandler
final class SafeScriptMessageHandler: NSObject, WKScriptMessageHandler {
weak var delegate: WKScriptMessageHandler?
init(delegate: WKScriptMessageHandler) {
self.delegate = delegate
super.init()
}

func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
delegate?.userContentController(
userContentController, didReceive: message
)
}
}
12 changes: 7 additions & 5 deletions Sources/App/WebView/WebViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,14 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
config.mediaTypesRequiringUserActionForPlayback = []

let userContentController = WKUserContentController()
userContentController.add(self, name: "getExternalAuth")
userContentController.add(self, name: "revokeExternalAuth")
userContentController.add(self, name: "externalBus")
userContentController.add(self, name: "updateThemeColors")
userContentController.add(self, name: "logError")
let safeScriptMessageHandler = SafeScriptMessageHandler(delegate: self)
userContentController.add(safeScriptMessageHandler, name: "getExternalAuth")
userContentController.add(safeScriptMessageHandler, name: "revokeExternalAuth")
userContentController.add(safeScriptMessageHandler, name: "externalBus")
userContentController.add(safeScriptMessageHandler, name: "updateThemeColors")
userContentController.add(safeScriptMessageHandler, name: "logError")

guard let wsBridgeJSPath = Bundle.main.path(forResource: "WebSocketBridge", ofType: "js"),
let wsBridgeJS = try? String(contentsOfFile: wsBridgeJSPath) else {
Expand Down
53 changes: 44 additions & 9 deletions Sources/App/WebView/WebViewWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class WebViewWindowController {
// not changing anything, but handle the promises
updateRootViewController(to: rootController)
} else {
if let webViewController = WebViewController(restoring: .init(restorationActivity)) {
if let webViewController = makeWebViewIfNotInCache(restorationType: .init(restorationActivity)) {
updateRootViewController(to: webViewNavigationController(rootViewController: webViewController))
} else {
updateRootViewController(to: OnboardingNavigationViewController(onboardingStyle: .initial))
Expand All @@ -85,6 +85,26 @@ class WebViewWindowController {
}
}

private func makeWebViewIfNotInCache(
restorationType: WebViewController.RestorationType?,
shouldLoadImmediately: Bool = false
) -> WebViewController? {
if let server = restorationType?.server ?? Current.servers.all.first {
if let cachedController = cachedWebViewControllers[server.identifier] {
return cachedController
} else {
let newController = WebViewController(
restoring: restorationType,
shouldLoadImmediately: shouldLoadImmediately
)
cachedWebViewControllers[server.identifier] = newController
return newController
}
} else {
return nil
}
}

func present(_ viewController: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) {
presentedViewController?.present(viewController, animated: animated, completion: completion)
}
Expand Down Expand Up @@ -130,12 +150,19 @@ class WebViewWindowController {
return .value(controller)
}

cachedWebViewControllers[controller.server.identifier] = controller

let (promise, resolver) = Guarantee<WebViewController>.pending()

let perform = { [self] in
let newController = cachedWebViewControllers[server.identifier] ?? WebViewController(server: server)
let newController: WebViewController = {
if let cachedController = cachedWebViewControllers[server.identifier] {
return cachedController
} else {
let newController = WebViewController(server: server)
cachedWebViewControllers[server.identifier] = newController
return newController
}
}()

updateRootViewController(to: webViewNavigationController(rootViewController: newController))
resolver(newController)
}
Expand Down Expand Up @@ -251,6 +278,10 @@ class WebViewWindowController {
)
}

func clearCachedControllers() {
cachedWebViewControllers = [:]
}

private func open(
from: OpenSource,
server: Server,
Expand Down Expand Up @@ -412,7 +443,11 @@ extension WebViewWindowController: OnboardingStateObserver {
return
}

cachedWebViewControllers.removeAll()
onboardingPreloadWebViewController = nil
// Remove cached webview for servers that don't exist anymore
cachedWebViewControllers = cachedWebViewControllers.filter({ serverIdentifier, _ in
Current.servers.all.contains(where: { $0.identifier == serverIdentifier })
})

if Current.servers.all.isEmpty {
let controller = OnboardingNavigationViewController(onboardingStyle: .initial)
Expand All @@ -439,8 +474,8 @@ extension WebViewWindowController: OnboardingStateObserver {
open(server: newServer)
}
case .didConnect:
onboardingPreloadWebViewController = WebViewController(
restoring: .init(restorationActivity),
onboardingPreloadWebViewController = makeWebViewIfNotInCache(
restorationType: .init(restorationActivity),
shouldLoadImmediately: true
)
case .complete:
Expand All @@ -450,8 +485,8 @@ extension WebViewWindowController: OnboardingStateObserver {
if let preload = onboardingPreloadWebViewController {
controller = preload
} else {
controller = WebViewController(
restoring: .init(restorationActivity),
controller = makeWebViewIfNotInCache(
restorationType: .init(restorationActivity),
shouldLoadImmediately: true
)
restorationActivity = nil
Expand Down

0 comments on commit 9a83285

Please sign in to comment.