Skip to content
This repository was archived by the owner on Aug 7, 2021. It is now read-only.

iOS delegate implementation causes TypeError: Attempted to assign to readonly property. #70

Closed
bnussey opened this issue Feb 9, 2017 · 17 comments

Comments

@bnussey
Copy link

bnussey commented Feb 9, 2017

After running npm run start-ios-bundle I get the following:

CONSOLE LOG file:///app/bundle.js:86026:16: Handler onNavigatedTo navigatedTo
1   0x104f4c33c -[TNSRuntime executeModule:referredBy:]
2   0x10347d519 main
3   0x108d2c68d start
4   0x1
file:///app/bundle.js:22761:25: JS ERROR TypeError: Attempted to assign to readonly property.
Feb  8 22:54:53 Blakes-MacBook-Pro-2 com.apple.CoreSimulator.SimDevice.C4495AD9-F0EE-453D-B745-8CAFA277F87F.launchd_sim[78098] (UIKitApplication:com.stageme.app[0x6f95][7930]): Service exited due to Segmentation fault: 11

This error relates to this code which is from my project, not a plugin:

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_file_system__ = __webpack_require__(/*! file-system */ 45);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_file_system___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_file_system__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_platform__ = __webpack_require__(/*! platform */ 10);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_platform___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_platform__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__shared_utils_using_simulator_util__ = __webpack_require__(/*! ../shared/utils/using-simulator.util */ 716);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__shared_utils_os_version_util__ = __webpack_require__(/*! ../shared/utils/os-version.util */ 715);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_timer__ = __webpack_require__(/*! timer */ 176);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_timer___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4_timer__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return SpotAssetManager; });
/* unused harmony export SpotAssetContainer */
/* unused harmony export SpotAssetDownloadTask */
/* unused harmony export AVAssetDownloadDelegateImpl */
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};

I've tried adding "noImplicitUseStrict": true, to my tsconfig, but no success.

I am using the standard webpack.common.js exactly like nativescript-sdk-examples-ng and here is my tsconfig:

{
    "compilerOptions": {
        "noEmitOnError": true,
        "noEmitHelpers": true,
        "target": "es5",
        "module": "commonjs",
        "declaration": false,
        "noImplicitAny": false,
        "noImplicitUseStrict": true,
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        // "importHelpers": true,
        "pretty": true,
        // "outDir": "bin/dist",
        /* enable these if you want to use the tns-platform-declarations */
        "lib": [
            "es2016"
        ],
        "skipLibCheck": true
    },
    "exclude": [
        "node_modules",
        "platforms",
        "**/*.aot.ts"
    ]
}
@bnussey
Copy link
Author

bnussey commented Feb 9, 2017

Ok so if I comment out the code that resulted in the above code from bundle.js I get the same error from another bit of my project code..

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_platform__ = __webpack_require__(/*! platform */ 10);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_platform___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_platform__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_ui_gestures__ = __webpack_require__(/*! ui/gestures */ 68);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_ui_gestures___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_ui_gestures__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__angular_core__ = __webpack_require__(/*! @angular/core */ 0);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_ui_page__ = __webpack_require__(/*! ui/page */ 1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_ui_page___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3_ui_page__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__angular_router__ = __webpack_require__(/*! @angular/router */ 15);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__core_config__ = __webpack_require__(/*! ../../../../../core/config */ 86);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_ui_web_view__ = __webpack_require__(/*! ui/web-view */ 369);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_ui_web_view___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6_ui_web_view__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_utils_utils__ = __webpack_require__(/*! utils/utils */ 4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_utils_utils___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7_utils_utils__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__services_commerce_commerce_service__ = __webpack_require__(/*! ../../../../../services/commerce/commerce.service */ 335);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__core_pub_sub_service__ = __webpack_require__(/*! ../../../../../core/pub-sub.service */ 6);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__services_analytics_analytics_service__ = __webpack_require__(/*! ../../../../../services/analytics/analytics.service */ 2);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_11__models_gestures_models__ = __webpack_require__(/*! ../../../../../models/gestures.models */ 137);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return SpotWebViewComponent; });
/* unused harmony export UIScrollViewDelegateImpl */
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};

@bnussey
Copy link
Author

bnussey commented Feb 9, 2017

Ok found that the consistent thing between these two files is that they both have delegates. Commenting out the delegates in the files results in it working.
I then moved the delegates out to their own files, but now the error is back. Any ideas on how to get this working?

Delegate code:

import { SpotAssetContainer, SpotAssetDownloadTask, SpotAssetDownloadTaskStatus } from "../models/asset-manager.models"

export class AVAssetDownloadDelegateImpl extends NSObject implements AVAssetDownloadDelegate {

    wrapper;

    public static ObjCProtocols = [AVAssetDownloadDelegate] // define our native protocols

    static new(): AVAssetDownloadDelegateImpl {
        return <AVAssetDownloadDelegateImpl>super.new() // calls new() on the NSObject
    }

    URLSessionAssetDownloadTaskDidFinishDownloadingToURL(session: NSURLSession, assetDownloadTask: AVAssetDownloadTask, location: NSURL): void {

        console.log("downloadComplete");

        let _assetDownloadTask: SpotAssetDownloadTask = this.wrapper.assetDownloadTasks.find(a => a.assetId === assetDownloadTask.taskDescription);

        if (_assetDownloadTask) {
            console.log("Asset download complete: " + _assetDownloadTask.assetId);
            _assetDownloadTask.status = SpotAssetDownloadTaskStatus.completed;
            this.wrapper.assetsDownloading -= 1;
            _assetDownloadTask.downloadFinishTime = Date.now();
            this.wrapper.calculateFileDownloadSpeed(location.relativePath, _assetDownloadTask);
        }
    }

    URLSessionAssetDownloadTaskDidLoadTimeRangeTotalTimeRangesLoadedTimeRangeExpectedToLoad?(session: NSURLSession, assetDownloadTask: AVAssetDownloadTask, timeRange: CMTimeRange, loadedTimeRanges: NSArray<NSValue>, timeRangeExpectedToLoad: CMTimeRange): void {

        let _asset: SpotAssetContainer = this.wrapper.assets.find(a => a.asset.id === assetDownloadTask.taskDescription);
        if (_asset) {
            let loadedSeconds = CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange));
            console.log("loadedSeconds " + loadedSeconds);
            if (loadedSeconds >= 10) {
                console.log("stopping download now that it is over 10 seconds");
                assetDownloadTask.cancel();
            }
        }

    }

}

Bundle delegate code:

/* 633 */
/* exports provided: AVAssetDownloadDelegateImpl */
/* exports used: AVAssetDownloadDelegateImpl */
/*!************************************************!*\
  !*** ./delegates/avasset-download-delegate.ts ***!
  \************************************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__models_asset_manager_models__ = __webpack_require__(/*! ../models/asset-manager.models */ 169);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return AVAssetDownloadDelegateImpl; });
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};

var AVAssetDownloadDelegateImpl = (function (_super) {
    __extends(AVAssetDownloadDelegateImpl, _super);
    function AVAssetDownloadDelegateImpl() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    AVAssetDownloadDelegateImpl.new = function () {
        return _super.new.call(this); // calls new() on the NSObject
    };
    AVAssetDownloadDelegateImpl.prototype.URLSessionAssetDownloadTaskDidFinishDownloadingToURL = function (session, assetDownloadTask, location) {
        console.log("downloadComplete");
        var _assetDownloadTask = this.wrapper.assetDownloadTasks.find(function (a) { return a.assetId === assetDownloadTask.taskDescription; });
        if (_assetDownloadTask) {
            console.log("Asset download complete: " + _assetDownloadTask.assetId);
            _assetDownloadTask.status = __WEBPACK_IMPORTED_MODULE_0__models_asset_manager_models__["b" /* SpotAssetDownloadTaskStatus */].completed;
            this.wrapper.assetsDownloading -= 1;
            _assetDownloadTask.downloadFinishTime = Date.now();
            this.wrapper.calculateFileDownloadSpeed(location.relativePath, _assetDownloadTask);
        }
    };
    AVAssetDownloadDelegateImpl.prototype.URLSessionAssetDownloadTaskDidLoadTimeRangeTotalTimeRangesLoadedTimeRangeExpectedToLoad = function (session, assetDownloadTask, timeRange, loadedTimeRanges, timeRangeExpectedToLoad) {
        var _asset = this.wrapper.assets.find(function (a) { return a.asset.id === assetDownloadTask.taskDescription; });
        if (_asset) {
            var loadedSeconds = CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange));
            console.log("loadedSeconds " + loadedSeconds);
            if (loadedSeconds >= 10) {
                console.log("stopping download now that it is over 10 seconds");
                assetDownloadTask.cancel();
            }
        }
    };
    return AVAssetDownloadDelegateImpl;
}(NSObject));

AVAssetDownloadDelegateImpl.ObjCProtocols = [AVAssetDownloadDelegate]; // define our native protocols
//# sourceMappingURL=avasset-download-delegate.js.map

/***/ }),

@bnussey bnussey changed the title TypeError: Attempted to assign to readonly property. iOS delegate implementation causes TypeError: Attempted to assign to readonly property. Feb 9, 2017
@hdeshev
Copy link
Contributor

hdeshev commented Feb 9, 2017

@bnussey could you post a sample project on GitHub, so I can reproduce the error?

@bnussey
Copy link
Author

bnussey commented Feb 9, 2017

Hey @hdeshev all you need to do is add a delegate like I have above and bundle and then try and run. I used this as a reference - https://docs.nativescript.org/runtimes/ios/how-to/ObjC-Subclassing.html#typescript-delegate-example

@bnussey
Copy link
Author

bnussey commented Feb 9, 2017

I have two different delegates and they both cause the error

@bnussey
Copy link
Author

bnussey commented Feb 10, 2017

If you'd like to replicate this on your side, use the following code:

app-delegate.ts

import * as app from "application";

export class iOSDelegate extends UIResponder implements UIApplicationDelegate {

    public static ObjCProtocols = [UIApplicationDelegate];

    applicationDidFinishLaunchingWithOptions?(application: UIApplication, launchOptions: NSDictionary<any, any>): boolean {
        // //https://docs.nativescript.org/runtimes/ios/how-to/ObjC-Subclassing
        return true;
    }

    applicationOpenURLSourceApplicationAnnotation?(application: UIApplication, url: NSURL, sourceApplication: string, annotation: any): boolean {
        return true;
    }

    applicationDidBecomeActive?(application: UIApplication): void {
        console.log("app became active");
        return;
    }

    applicationWillTerminate?(application: UIApplication): void {
        //Do something you want here
    }

    applicationDidEnterBackground(application: UIApplication): void {
        //Do something you want here
    }

}

export function setiOSDelegate() {
    app.ios.delegate = iOSDelegate;
}
main.aot.ts

import { platformNativeScript } from "nativescript-angular/platform-static";
import { AppModuleNgFactory } from "./app.module.ngfactory";
import { isAndroid, isIOS } from "platform";
import { setiOSDelegate } from "./core/app-delegate/app-delegate";

// setup ios delegate handlers (ios only)
if (isIOS) {
    setiOSDelegate();
}

platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);

@NathanaelA
Copy link

This is caused by the emitting of the helpers.

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};

Add a "noEmitHelpers": true to your tsconfig.json and your tsconfig.aot.json file.

@bnussey
Copy link
Author

bnussey commented Feb 10, 2017

This has fixed it, thank you @NathanaelA

@slmolloy
Copy link

I am seeing the exact same issue that @bnussey was seeing with the UIApplicationDelegate. I added the "noEmitHelpers": true that @NathanaelA suggested but I am still getting a segmentation fault on app launch but for a different reason.

file:///app/bundle.js:53371:31: JS ERROR ReferenceError: Can't find variable: __assign

The code in the bundle.js is:

Object.defineProperty(exports, "__esModule", { value: true });
var base_1 = __webpack_require__(/*! ./base */ 582);
exports.environment = __assign({}, base_1.baseConfig);

@bnussey
Copy link
Author

bnussey commented Aug 29, 2017

Hi @slmolloy did you do it in your tsconfig.aot.json or just your tsconfig.json ?

@slmolloy
Copy link

My tsconfig.aot.json extends tsconfig.json. I tried in each one independently as well as both. Each attempt resulted in the same error.

@danielgek
Copy link

@slmolloy any luck on this ? having the same issue and i'm getting crazy with tsconfigs

@slmolloy
Copy link

slmolloy commented Sep 3, 2017

@danielgek One of my teammates figured it out. We did not use the "noEmitHelpers": true as that caused more problems for us. These links talked about the issue: #8 and http://fluentreports.com/blog/?p=342.

In the second link at the end of the post, there is info on the crashing on startup. We used the eliminateExtends method.

/**********************************************************************************
 * (c) 2016, Master Technology
 * Licensed under the MIT license or contact me for a Support or Commercial License
 *
 * I do contract work in most languages, so let me solve your problems!
 *
 * Any questions please feel free to email me
 * Version 1.0.2                                      [email protected]
 *********************************************************************************/
"use strict";

function eliminateExtends(pre) {
    var i = pre.indexOf('var __extends = ');
    if (i === -1) {
      return pre;
    }
    var x = pre.indexOf('};',i);
    return pre.substring(0,i) + pre.substring(x+2);
}
module.exports = eliminateExtends;

We then updated our webpack.config.js by adding a resolveLoader:

resolveLoader: {
    modules: ["node_modules", resolve(__dirname, "webpack-loaders")]
},

And added eliminate-extends to the .ts rule:

{
    test: /\.ts$/,
    loaders: [
        "eliminate-extends"
    ]
}

@lambourn
Copy link

Note to future readers:

we had the same issue a while back and @slmolloy 's comment above did fix the issue.

The issue reappeared by itself after we migrated to Webpack 4 / nativescript-dev-webpack 0.15.1

For some strange reason, the .js file that implements the eliminate-extends was automatically deleted from the file system after the webpack.config.js was updated. Not sure if this is by design, but maybe the webpack config update script does this.

After manual recreation of the .js file and adjustment of the webpack.config.js it works again.

IMHO the root issue (iOS delegate implementation causes TypeError) is still a bug.

@miguelopezv
Copy link

@lambourn can you please elaborate on how you implement @slmolloy 's solution? i've created the eliminateExtends.js file on my app root folder, then added resolve(__dirname, "webpack-loader") to my webpack config file (do I need to install something to make it work?) and finally added this to the .ts rule:

test: /.ts$/,
use: [
    {loader: "eliminateExtends"},
    ...
]

but al iget is that it can't resolve the file:
ERROR in Entry module not found: Error: Can't resolve 'eliminateExtends' in '/Volumes/Users/.../app'

@lambourn
Copy link

@miguelopezv sure.

In my case, the webpack.config.js is based on the default config for NativeScript-Angular projects. The elimiate-extends.js is stored in a ./lib folder in the root of the project.

In the webpack.config.js I added

resolveLoader: {
    modules: ["node_modules", resolve("./lib")],
    symlinks: false
},

to ensure it gets found.

Then in the module.rules it is added to the test for .ts files:

// Compile TypeScript files with ahead-of-time compiler.
{
    test: /.ts$/, use: [
        "eliminate-extends",
        "nativescript-dev-webpack/moduleid-compat-loader",
        "@ngtools/webpack",
    ]
},

@lambourn
Copy link

fyi - this issue still exists with {N} 5.2 and nativescript-dev-webpack 0.20.2

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants