UNPKG

@nativescript/core

Version:

A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.

445 lines • 18.3 kB
import { profile } from '../profiling'; import { isEmbedded } from '../ui/embedding'; import { IOSHelper } from '../ui/core/view/view-helper'; import * as Utils from '../utils'; import { ApplicationCommon } from './application-common'; var CADisplayLinkTarget = /** @class */ (function (_super) { __extends(CADisplayLinkTarget, _super); function CADisplayLinkTarget() { return _super !== null && _super.apply(this, arguments) || this; } CADisplayLinkTarget.initWithOwner = function (owner) { var target = CADisplayLinkTarget.new(); target._owner = owner; return target; }; CADisplayLinkTarget.prototype.onDisplayed = function (link) { link.invalidate(); var owner = this._owner.deref(); if (!owner) { return; } owner.displayedOnce = true; owner.notify({ eventName: owner.displayedEvent, object: owner, ios: UIApplication.sharedApplication, }); owner.displayedLinkTarget = null; owner.displayedLink = null; }; CADisplayLinkTarget.ObjCExposedMethods = { onDisplayed: { returns: interop.types.void, params: [CADisplayLink] }, }; return CADisplayLinkTarget; }(NSObject)); var NotificationObserver = /** @class */ (function (_super) { __extends(NotificationObserver, _super); function NotificationObserver() { return _super !== null && _super.apply(this, arguments) || this; } NotificationObserver.initWithCallback = function (onReceiveCallback) { var observer = _super.new.call(this); observer._onReceiveCallback = onReceiveCallback; return observer; }; NotificationObserver.prototype.onReceive = function (notification) { this._onReceiveCallback(notification); }; NotificationObserver.ObjCExposedMethods = { onReceive: { returns: interop.types.void, params: [NSNotification] }, }; return NotificationObserver; }(NSObject)); var Responder = /** @class */ (function (_super) { __extends(Responder, _super); function Responder() { return _super !== null && _super.apply(this, arguments) || this; } Object.defineProperty(Responder.prototype, "window", { get: function () { return Application.ios.window; }, set: function (value) { // NOOP }, enumerable: true, configurable: true }); Responder.ObjCProtocols = [UIApplicationDelegate]; return Responder; }(UIResponder)); export class iOSApplication extends ApplicationCommon { /** * @internal - should not be constructed by the user. */ constructor() { super(); this._delegateHandlers = new Map(); this._notificationObservers = []; this.displayedOnce = false; this.addNotificationObserver(UIApplicationDidFinishLaunchingNotification, this.didFinishLaunchingWithOptions.bind(this)); this.addNotificationObserver(UIApplicationDidBecomeActiveNotification, this.didBecomeActive.bind(this)); this.addNotificationObserver(UIApplicationDidEnterBackgroundNotification, this.didEnterBackground.bind(this)); this.addNotificationObserver(UIApplicationWillTerminateNotification, this.willTerminate.bind(this)); this.addNotificationObserver(UIApplicationDidReceiveMemoryWarningNotification, this.didReceiveMemoryWarning.bind(this)); this.addNotificationObserver(UIApplicationDidChangeStatusBarOrientationNotification, this.didChangeStatusBarOrientation.bind(this)); } getRootView() { return this._rootView; } resetRootView(view) { super.resetRootView(view); this.setWindowContent(); } run(entry) { this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry; this.started = true; if (this.nativeApp) { this.runAsEmbeddedApp(); } else { this.runAsMainApp(); } } runAsMainApp() { UIApplicationMain(0, null, null, this.delegate ? NSStringFromClass(this.delegate) : NSStringFromClass(Responder)); } runAsEmbeddedApp() { // TODO: this rootView should be held alive until rootController dismissViewController is called. const rootView = this.createRootView(this._rootView, true); if (!rootView) { return; } this._rootView = rootView; // Attach to the existing iOS app const window = Utils.ios.getWindow(); if (!window) { return; } const rootController = window.rootViewController; if (!rootController) { return; } const controller = this.getViewController(rootView); const embedderDelegate = NativeScriptEmbedder.sharedInstance().delegate; rootView._setupAsRootView({}); rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => { const userInterfaceStyle = controller.traitCollection.userInterfaceStyle; const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle); this.setSystemAppearance(newSystemAppearance); }); if (embedderDelegate) { this.setViewControllerView(rootView); embedderDelegate.presentNativeScriptApp(controller); } else { const visibleVC = Utils.ios.getVisibleViewController(rootController); visibleVC.presentViewControllerAnimatedCompletion(controller, true, null); } this.initRootView(rootView); this.notifyAppStarted(); } getViewController(rootView) { let viewController = rootView.viewController || rootView.ios; if (!(viewController instanceof UIViewController)) { // We set UILayoutViewController dynamically to the root view if it doesn't have a view controller // At the moment the root view doesn't have its native view created. We set it in the setViewControllerView func viewController = IOSHelper.UILayoutViewController.initWithOwner(new WeakRef(rootView)); rootView.viewController = viewController; } return viewController; } setViewControllerView(view) { const viewController = view.viewController || view.ios; const nativeView = view.ios || view.nativeViewProtected; if (!nativeView || !viewController) { throw new Error('Root should be either UIViewController or UIView'); } if (viewController instanceof IOSHelper.UILayoutViewController) { viewController.view.addSubview(nativeView); } } setMaxRefreshRate(options) { const adjustRefreshRate = () => { if (!this.displayedLink) { return; } const minFrameRateDisabled = NSBundle.mainBundle.objectForInfoDictionaryKey('CADisableMinimumFrameDurationOnPhone'); if (minFrameRateDisabled) { let max = 120; const deviceMaxFrames = Utils.ios.getMainScreen().maximumFramesPerSecond; if (options?.max) { if (deviceMaxFrames) { // iOS 10.3 max = options.max <= deviceMaxFrames ? options.max : deviceMaxFrames; } else if (this.displayedLink.preferredFramesPerSecond) { // iOS 10.0 max = options.max <= this.displayedLink.preferredFramesPerSecond ? options.max : this.displayedLink.preferredFramesPerSecond; } } if (Utils.SDK_VERSION >= 15 || __VISIONOS__) { const min = options?.min || max / 2; const preferred = options?.preferred || max; this.displayedLink.preferredFrameRateRange = CAFrameRateRangeMake(min, max, preferred); } else { this.displayedLink.preferredFramesPerSecond = max; } } }; if (this.displayedOnce) { adjustRefreshRate(); return; } this.displayedLinkTarget = CADisplayLinkTarget.initWithOwner(new WeakRef(this)); this.displayedLink = CADisplayLink.displayLinkWithTargetSelector(this.displayedLinkTarget, 'onDisplayed'); adjustRefreshRate(); this.displayedLink.addToRunLoopForMode(NSRunLoop.mainRunLoop, NSDefaultRunLoopMode); this.displayedLink.addToRunLoopForMode(NSRunLoop.mainRunLoop, UITrackingRunLoopMode); } get rootController() { return this.window?.rootViewController; } get nativeApp() { return UIApplication.sharedApplication; } get window() { // TODO: consideration // may not want to cache this value given the potential of multiple scenes // particularly with SwiftUI app lifecycle based apps if (!this._window) { // Note: NativeScriptViewFactory.getKeyWindow will always be used in SwiftUI app lifecycle based apps this._window = Utils.ios.getWindow(); } return this._window; } get delegate() { return this._delegate; } set delegate(value) { if (this._delegate !== value) { this._delegate = value; } } addDelegateHandler(methodName, handler) { // safe-guard against invalid handlers if (typeof handler !== 'function') { return; } // ensure we have a delegate this.delegate ?? (this.delegate = Responder); const handlers = this._delegateHandlers.get(methodName) ?? []; if (!this._delegateHandlers.has(methodName)) { const originalHandler = this.delegate.prototype[methodName]; if (originalHandler) { // if there is an original handler, we add it to the handlers array to be called first. handlers.push(originalHandler); } // replace the original method implementation with one that will call all handlers. this.delegate.prototype[methodName] = function (...args) { let res; for (const handler of handlers) { if (typeof handler !== 'function') { continue; } res = handler.apply(this, args); } return res; }; // store the handlers this._delegateHandlers.set(methodName, handlers); } handlers.push(handler); } getNativeApplication() { return this.nativeApp; } addNotificationObserver(notificationName, onReceiveCallback) { const observer = NotificationObserver.initWithCallback(onReceiveCallback); NSNotificationCenter.defaultCenter.addObserverSelectorNameObject(observer, 'onReceive', notificationName, null); this._notificationObservers.push(observer); return observer; } removeNotificationObserver(observer, notificationName) { const index = this._notificationObservers.indexOf(observer); if (index >= 0) { this._notificationObservers.splice(index, 1); NSNotificationCenter.defaultCenter.removeObserverNameObject(observer, notificationName, null); } } getSystemAppearance() { // userInterfaceStyle is available on UITraitCollection since iOS 12. if ((!__VISIONOS__ && Utils.SDK_VERSION <= 11) || !this.rootController) { return null; } const userInterfaceStyle = this.rootController.traitCollection.userInterfaceStyle; return this.getSystemAppearanceValue(userInterfaceStyle); } getSystemAppearanceValue(userInterfaceStyle) { switch (userInterfaceStyle) { case 2 /* UIUserInterfaceStyle.Dark */: return 'dark'; case 1 /* UIUserInterfaceStyle.Light */: case 0 /* UIUserInterfaceStyle.Unspecified */: return 'light'; } } getOrientation() { let statusBarOrientation; if (__VISIONOS__) { statusBarOrientation = NativeScriptEmbedder.sharedInstance().windowScene.interfaceOrientation; } else { statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation; } return this.getOrientationValue(statusBarOrientation); } getOrientationValue(orientation) { switch (orientation) { case 3 /* UIInterfaceOrientation.LandscapeRight */: case 4 /* UIInterfaceOrientation.LandscapeLeft */: return 'landscape'; case 2 /* UIInterfaceOrientation.PortraitUpsideDown */: case 1 /* UIInterfaceOrientation.Portrait */: return 'portrait'; case 0 /* UIInterfaceOrientation.Unknown */: return 'unknown'; } } notifyAppStarted(notification) { const root = this.notifyLaunch({ ios: notification?.userInfo?.objectForKey('UIApplicationLaunchOptionsLocalNotificationKey') ?? null, }); if (this._window) { if (root !== null && !isEmbedded()) { this.setWindowContent(root); } } else { this._window = this.window; // UIApplication.sharedApplication.keyWindow; } } _onLivesync(context) { // Handle application root module const isAppRootModuleChanged = context && context.path && context.path.includes(this.getMainEntry().moduleName) && context.type !== 'style'; // Set window content when: // + Application root module is changed // + View did not handle the change // Note: // The case when neither app root module is changed, nor livesync is handled on View, // then changes will not apply until navigate forward to the module. if (isAppRootModuleChanged || (this._rootView && !this._rootView._onLivesync(context))) { this.setWindowContent(); } } setWindowContent(view) { if (this._rootView) { // if we already have a root view, we reset it. this._rootView._onRootViewReset(); } const rootView = this.createRootView(view); const controller = this.getViewController(rootView); this._rootView = rootView; // setup view as styleScopeHost rootView._setupAsRootView({}); this.setViewControllerView(rootView); const win = this.window; const haveController = win.rootViewController !== null; win.rootViewController = controller; if (!haveController) { win.makeKeyAndVisible(); } this.initRootView(rootView); rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => { const userInterfaceStyle = controller.traitCollection.userInterfaceStyle; const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle); this.setSystemAppearance(newSystemAppearance); }); } // Observers didFinishLaunchingWithOptions(notification) { this.setMaxRefreshRate(); // ensures window is assigned to proper window scene this._window = this.window; if (!this._window) { // if still no window, create one this._window = UIWindow.alloc().initWithFrame(UIScreen.mainScreen.bounds); } if (!__VISIONOS__) { this.window.backgroundColor = Utils.SDK_VERSION <= 12 || !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor; } this.notifyAppStarted(notification); } didBecomeActive(notification) { const additionalData = { ios: UIApplication.sharedApplication, }; this.setInBackground(false, additionalData); this.setSuspended(false, additionalData); const rootView = this._rootView; if (rootView && !rootView.isLoaded) { rootView.callLoaded(); } } didEnterBackground(notification) { const additionalData = { ios: UIApplication.sharedApplication, }; this.setInBackground(true, additionalData); this.setSuspended(true, additionalData); const rootView = this._rootView; if (rootView && rootView.isLoaded) { rootView.callUnloaded(); } } willTerminate(notification) { this.notify({ eventName: this.exitEvent, object: this, ios: this.ios, }); // const rootView = this._rootView; // if (rootView && rootView.isLoaded) { // rootView.callUnloaded(); // } } didReceiveMemoryWarning(notification) { this.notify({ eventName: this.lowMemoryEvent, object: this, ios: this.ios, }); } didChangeStatusBarOrientation(notification) { const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation; const newOrientation = this.getOrientationValue(statusBarOrientation); this.setOrientation(newOrientation); } get ios() { // ensures Application.ios is defined when running on iOS return this; } } __decorate([ profile, __metadata("design:type", Function), __metadata("design:paramtypes", [NSNotification]), __metadata("design:returntype", void 0) ], iOSApplication.prototype, "didFinishLaunchingWithOptions", null); __decorate([ profile, __metadata("design:type", Function), __metadata("design:paramtypes", [NSNotification]), __metadata("design:returntype", void 0) ], iOSApplication.prototype, "didBecomeActive", null); const iosApp = new iOSApplication(); // Attach on global, so it can also be overwritten to implement different logic based on flavor global.__onLiveSyncCore = function (context) { iosApp._onLivesync(context); }; export * from './application-common'; export const Application = iosApp; export const AndroidApplication = undefined; //# sourceMappingURL=application.ios.js.map