Nativescript: [3.0 Feature Request]: iOS Delegate Service

Created on 26 Apr 2017  路  7Comments  路  Source: NativeScript/NativeScript

There are a lot of plugins out there that explicitly set the application delegate and it is extremely troublesome getting them working together.

I propose a "splitter" to split/broadcast the delegate event. The class I use for all of my custom plugins looks like this (which I point application.ios.delegate to):

export class Delegate extends UIResponder implements UIApplicationDelegate {
    public static ObjCProtocols = [UIApplicationDelegate];
    protected static _queue: Object = {};

    applicationOpenURLOptions(application: typeof UIApplication, url: string, options: typeof NSDictionary) {
        return Delegate._promise("applicationOpenURLOptions", { application, url, options });
    }

    applicationDidFinishLaunchingWithOptions(application: typeof UIApplication, launchOptions: any) {
        return Delegate._promise("applicationDidFinishLaunchingWithOptions", { application, launchOptions });
    }

    // etc...

    protected static _promise(fn: string, args: any) {
        let constants = {
            // keep a copy of the call-time application state
            state: args.application.applicationState
        };

        let promise = Promise.resolve().then(() => {
            return { fn: fn, args: args, constants: constants };
        });

        let entry;

        if (!(entry = this._queue[fn])) {
            entry = { callbacks: [], promise: promise };
            this._queue[fn] = entry;
            return entry;
        }

        entry.promise = promise;

        if (entry.callbacks.length > 0) {
            entry.callbacks.forEach(function(callback) {
                entry.promise.then(callback);
            });
        }

        return entry;
    }

    public static apply(fn: string, callback: any) {
        let entry;

        if (!(entry = this._queue[fn])) {
            entry = this._queue[fn] = { callbacks: [], promise: false };
        }

        if (!entry.promise) {
            entry.callbacks.push(callback);
        } else {
            entry.promise.then(callback);
        }

        return entry;
    }
}

Then I add this in my plugins' main script and import that before application.start:

Delegate.apply("applicationDidFinishLaunchingWithOptions", (event) => {
    // event.args contains applicationDidFinishLaunchingWithOptions native arguments...
});

Works great for me. Maybe the only thing missing is a function to intercept old implementations for backwards-compatibility. What are the community's thoughts on this concept as a core feature?


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

feature help wanted ios

Most helpful comment

I am fan of the idea.
I am linking the last time there was some development on app delegates:
https://github.com/NativeScript/NativeScript/issues/432
I believe we can make plugins register protocols and methods (event like) on a common app delegate before the application.start() is called.

Here is one plugin I know merging AppDelegates:
https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/firebase.ios.js#L291

There are probably more, can we compile a list?

All 7 comments

I am fan of the idea.
I am linking the last time there was some development on app delegates:
https://github.com/NativeScript/NativeScript/issues/432
I believe we can make plugins register protocols and methods (event like) on a common app delegate before the application.start() is called.

Here is one plugin I know merging AppDelegates:
https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/firebase.ios.js#L291

There are probably more, can we compile a list?

I very much need "applicationOpenURLOptions" for the Auth0 plugin... @vbresults sample seems to do the callback just fine, but whats the plan going forward to make this a bit easier to implement for users?

you got give also my plugin a try for ease URL handling: https://github.com/hypery2k/nativescript-urlhandler

The approach taken by @hypery2k seems the best one in my opinion, however currently it has some flaws because of a lack of a unified way to manage the application's delegate.

The approach revolves around trying to chain the methods with one another like so. This idea is exceptional, however is quickly undermined by something like the firebase or the facebook plugins.

My proposition revolves around the same idea backed up by some code in the tns-core-modules.

  1. Adapt the chain logic inside the tns-core-modules
  2. Modify the require("application").ios.delegate setter so that it becomes something like this:
set delegate(value: typeof UIApplicationDelegate) {
    Object.getOwnPropertyNames(value).forEach(methodName => {
        enableMultipleOverridesFor(this._delegate, methodName);
    });
}

The benefits of this approach would be:

  1. One single instance of the application delegate with no way for external code (that is outside of tns-core-modules) to set or directly modify it directly (meaning no way for plugins to hinder each other's functionality anymore)
  2. Backwards compatibility - code like require("application").ios.delegate = MyCustomDelegate; will start working out-of-the-box.

The drawback of the approach would be:

  1. No way of controlling the order of the method invocations
  2. Code altering methods directly will have to be handled in some way

I don't know why. But the url-handler plugin seems not to work when also using https://github.com/alexziskind1/nativescript-oauth2

@Naxos84 this is because the current setter of IOSApplication.delegate will only accept a first value and discard following assignation attempts.
@hypery2k 's plugin (url-handler) tries to conserve existing methods of a previous delegate implementation such that all methods will be executed one after the other. but this relies on the assumption that the delegate implementation is thought as a method container whit no state.
@alexziskind1's Oauth2 plugin doesn't care about previous implementations and will just try to set the delegate with a class where methods actually need a specific state. This implementation is plainly incompatible with any other, and I was quite disappointed to see that in a plugin.

@SylannBin yes this is a known issue. A PR to fix would be greatly appreciated. Thanks!

Was this page helpful?
0 / 5 - 0 ratings