Files
orca/mobile/modules/orca-mobile-web-shell/ios/OrcaMobileWebShellModule.swift
T
Kelvin Amoaba 41867a2a85 perf(mobile): stop rebooting the Home spare page on every hop and after iOS memory warnings (#40270)
* perf(mobile): back off spare page refills after an unclaimed drop or memory warning

* fix(mobile): skip a spare whose store read outlived an unclaimed drop

* fix(mobile): arm the spare refill timer on whole milliseconds
2026-11-09 13:52:05 +00:00

110 lines
3.5 KiB
Swift

import ExpoModulesCore
import QuickLook
import UIKit
public class OrcaMobileWebShellModule: Module {
private var pdfPreview: MobileWebShellPdfPreview?
private var memoryWarningObserver: NSObjectProtocol?
public func definition() -> ModuleDefinition {
Name("OrcaMobileWebShell")
OnCreate {
memoryWarningObserver = NotificationCenter.default.addObserver(
forName: UIApplication.didReceiveMemoryWarningNotification,
object: nil,
queue: .main
) { _ in
MobileWebShellSparePool.shared.didReceiveMemoryWarning()
}
}
OnDestroy {
if let memoryWarningObserver {
NotificationCenter.default.removeObserver(memoryWarningObserver)
}
DispatchQueue.main.async {
MobileWebShellSparePool.shared.discardAll()
}
}
// Home boots the next one after its next foreground idle.
OnAppEntersBackground {
DispatchQueue.main.async {
MobileWebShellSparePool.shared.discardAll()
}
}
AsyncFunction("prepareSpareDocument") { (directory: String, sessionId: String) -> Bool in
MobileWebShellSparePool.shared.prepare(directory: directory, sessionId: sessionId)
}.runOnQueue(.main)
AsyncFunction("claimSpareDocument") { (directory: String) -> String? in
MobileWebShellSparePool.shared.claim(directory: directory)
}.runOnQueue(.main)
AsyncFunction("discardSpareDocument") {
MobileWebShellSparePool.shared.discardAll()
}.runOnQueue(.main)
// Home calls this once it has a paired host, so a user with nothing to open pays nothing.
Function("warmUp") {
// A second's delay keeps the warm-up off the launch's first frames.
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
MobileWebShellWarmUp.start()
}
}
AsyncFunction("presentPdf") { (url: URL) in
guard let presenter = self.appContext?.utilities?.currentViewController() else {
throw PdfPresenterUnavailableException()
}
let preview = MobileWebShellPdfPreview(url: url)
let controller = QLPreviewController()
controller.dataSource = preview
self.pdfPreview = preview
presenter.present(controller, animated: true)
}.runOnQueue(.main)
AsyncFunction("shareImage") { (url: URL, _: String) in
guard let presenter = self.appContext?.utilities?.currentViewController() else {
throw ImagePresenterUnavailableException()
}
presentImageShareSheet(url: url, from: presenter)
}.runOnQueue(.main)
AsyncFunction("saveImageToPhotos") { (url: URL, _: String, promise: Promise) in
saveImageToPhotoLibrary(url: url, promise: promise)
}
View(OrcaMobileWebShellView.self) {
Events("onLoadState", "onBridgeMessage", "onExternalNavigation", "onBackEdgeSwipe")
Prop("generationDirectory") { (view: OrcaMobileWebShellView, value: String) in
view.setGenerationDirectory(value)
}
Prop("sessionId") { (view: OrcaMobileWebShellView, value: String) in
view.setSessionId(value)
}
Prop("bridgeEnabled") { (view: OrcaMobileWebShellView, value: Bool) in
view.setBridgeEnabled(value)
}
Prop("backEdgeSwipe") { (view: OrcaMobileWebShellView, value: Bool) in
view.setBackEdgeSwipe(value)
}
AsyncFunction("postBridgeMessage") {
(view: OrcaMobileWebShellView, json: String, promise: Promise) in
try view.postBridgeMessage(json, promise: promise)
}
OnViewDidUpdateProps { (view: OrcaMobileWebShellView) in
view.propsDidUpdate()
}
}
}
}