@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
JavaScript
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