Files
orca/mobile/modules/orca-mobile-web-shell/ios/OrcaMobileWebShellModule.swift
T
Kelvin Amoaba a4291dbc4a fix(mobile): keep page builds where the OS doesn't purge them (#40524)
iOS emptied Library/Caches under storage pressure, and Android's cacheDir is
cleared by Clear cache, cleaner apps and low storage. Each purge cost a full
foreground download of the page build on the next open. Builds now live in
Application Support (excluded from backup) on iOS and filesDir on Android
(kept off device transfers), and the old cache folder moves over once.
2026-11-09 23:35:43 +00:00

115 lines
3.8 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)
// The generation store's root as a file URL, created and excluded from backups.
Function("generationStoreDirectory") { (name: String) throws -> String in
try MobileWebShellGenerationStoreDirectory.prepare(name: name)
}
// 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()
}
}
}
}