IonicでfcmのrequestPushPermissionIOS()を呼び出すのに手こずった話

# IonicのfcmでiOSにPUSH通知を送りたい
Ionicを利用してAndroid/iOSのアプリを作っていたときに困った問題。

*iOSにPUSH通知が受け取れない...*

AndroidではPUSH通知は受け取れている。
PUSH通知のための証明書は用意した。
`pod install`やらなんやらの問題も解決して、ビルドも成功している。
なのになぜ動かない...。

～しばらくして～

なるほど。

FCMプラグインのgithubを見ると
https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated

>On iOS, first run doesn't automatically request Push permission.

>The permission, as it is still required, may now be requested from javascript at any moment by executing:

>`//FCMPlugin.requestPushPermissionIOS( successCallback(), errorCallback(err) );`
>`FCMPlugin.requestPushPermissionIOS();`

とある。
ああそうか、iOSではPUSH通知の許可をユーザーに要求しないといけないのか。

じゃあIonicでこう書けばいいんだな。

```app.component.ts
this.fcm.requestPushPermissionIOS();
```

結果:
```
requestPushPermissionIOS() is not a function.
```

:thinking:

おかしいな、github見る限りはメソッドとして定義されているのに...?
pluginのソースコードを見てもちゃんと定義されているぞ...?

なんでだ...?

～またしばらくして～

あれ？
@ionic-native/fcm/ngx
の中身のコードだと、**requestPushPermissionIOS()が定義されてない**...だと...?

```@ionic-native/fcm/ngx/index.d.ts
export declare class FCM extends IonicNativePlugin {
    /**
     * Gets ios device's current APNS token
     *
     * @returns {Promise<string>} Returns a Promise that resolves with the APNS token
     */
    getAPNSToken(): Promise<string>;
    /**
     * Gets device's current registration id
     *
     * @returns {Promise<string>} Returns a Promise that resolves with the registration id token
     */
    getToken(): Promise<string>;
    /**
     * Event firing on the token refresh
     *
     * @returns {Observable<string>} Returns an Observable that notifies with the change of device's registration id
     */
    onTokenRefresh(): Observable<string>;
    /**
     * Subscribes you to a [topic](https://firebase.google.com/docs/notifications/android/console-topics)
     *
     * @param {string} topic Topic to be subscribed to
     *
     * @returns {Promise<any>} Returns a promise resolving in result of subscribing to a topic
     */
    subscribeToTopic(topic: string): Promise<any>;
    /**
     * Unsubscribes you from a [topic](https://firebase.google.com/docs/notifications/android/console-topics)
     *
     * @param {string} topic Topic to be unsubscribed from
     *
     * @returns {Promise<any>} Returns a promise resolving in result of unsubscribing from a topic
     */
    unsubscribeFromTopic(topic: string): Promise<any>;
    /**
     * Checking for permissions on iOS. On android, it always returns `true`.
     *
     * @returns {Promise<boolean | null>} Returns a Promise:
     * - true: push was allowed (or platform is android)
     * - false: push will not be available
     * - null: still not answered, recommended checking again later.
     */
    hasPermission(): Promise<boolean | null>;
    /**
     * Watch for incoming notifications
     *
     * @returns {Observable<any>} returns an object with data from the notification
     */
    onNotification(): Observable<NotificationData>;
    /**
     * Removes existing push notifications from the notifications center
     *
     * @returns {Promise<void>}
     */
    clearAllNotifications(): void;
    static ɵfac: ɵngcc0.ɵɵFactoryDef<FCM>;
    static ɵprov: ɵngcc0.ɵɵInjectableDef<FCM>;
}
```

なるほど,
Plugin自体には記述があっても、ionicのほうでは呼び出せないようになっているのか。

## @ionic-native/fcm/ngxのコードを書き替えて、requestPushPermissionIOS()を無理やり呼び出す

- 先ほどのコードに下記を追加。

```@ionic-native/fcm/ngx/index.d.ts
requestPushPermissionIOS(): void;
```

- 関連してそうなファイルをどんどん書き換える。

```@ionic-native/fcm/index.d.ts
requestPushPermissionIOS(): void;
```

```@ionic-native/fcm/__ivy_ngcc__/ngx/index.js
var FCM = /** @class */ (function (_super) {
    __extends(FCM, _super);
    function FCM() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    FCM.prototype.getAPNSToken = function () { return cordova(this, "getAPNSToken", {}, arguments); };
    FCM.prototype.getToken = function () { return cordova(this, "getToken", {}, arguments); };
    FCM.prototype.onTokenRefresh = function () { return cordova(this, "onTokenRefresh", { "observable": true }, arguments); };
    FCM.prototype.subscribeToTopic = function (topic) { return cordova(this, "subscribeToTopic", {}, arguments); };
    FCM.prototype.unsubscribeFromTopic = function (topic) { return cordova(this, "unsubscribeFromTopic", {}, arguments); };
    FCM.prototype.hasPermission = function () { return cordova(this, "hasPermission", {}, arguments); };
    FCM.prototype.onNotification = function () { return cordova(this, "onNotification", { "observable": true, "successIndex": 0, "errorIndex": 2 }, arguments); };
    FCM.prototype.clearAllNotifications = function () { return cordova(this, "clearAllNotifications", {}, arguments); };
    FCM.prototype.requestPushPermissionIOS = function () { return cordova(this, "requestPushPermissionIOS", {}, arguments); }; // この記述を追加
    FCM.pluginName = "FCM";
    FCM.plugin = "cordova-plugin-fcm-with-dependecy-updated";
    FCM.pluginRef = "FCMPlugin";
    FCM.repo = "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated";
    FCM.platforms = ["Android", "iOS"];
```

```@ionic-native/fcm/index.js
var FCM = /** @class */ (function (_super) {
    __extends(FCM, _super);
    function FCM() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    FCM.prototype.getAPNSToken = function () { return cordova(this, "getAPNSToken", {}, arguments); };
    FCM.prototype.getToken = function () { return cordova(this, "getToken", {}, arguments); };
    FCM.prototype.onTokenRefresh = function () { return cordova(this, "onTokenRefresh", { "observable": true }, arguments); };
    FCM.prototype.subscribeToTopic = function (topic) { return cordova(this, "subscribeToTopic", {}, arguments); };
    FCM.prototype.unsubscribeFromTopic = function (topic) { return cordova(this, "unsubscribeFromTopic", {}, arguments); };
    FCM.prototype.hasPermission = function () { return cordova(this, "hasPermission", {}, arguments); };
    FCM.prototype.onNotification = function () { return cordova(this, "onNotification", { "observable": true, "successIndex": 0, "errorIndex": 2 }, arguments); };
    FCM.prototype.clearAllNotifications = function () { return cordova(this, "clearAllNotifications", {}, arguments); };
    FCM.prototype.requestPushPermissionIOS = function () { return cordova(this, "requestPushPermissionIOS", {}, arguments); }; // この記述を追加
    FCM.pluginName = "FCM";
    FCM.plugin = "cordova-plugin-fcm-with-dependecy-updated";
    FCM.pluginRef = "FCMPlugin";
    FCM.repo = "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated";
    FCM.platforms = ["Android", "iOS"];
```

ここまでして、ようやく下記コードが正常に動きました。

```app.component.ts
this.fcm.requestPushPermissionIOS();
```

めでたしめでたし（？）

## おわりに
ionic / cordovaでのアプリ開発はplugin頼みのことが多い+iOS依存の課題で思わぬ工数を使ってしまうことがあるのでなかなか大変ですね。

今回の件も相当な時間を使って調べてようやく解決した問題でした。
（ほかに同じ問題で悩んで解決したような投稿がどこにもなかった）
この記事がどなたかのお役に立てれば幸いです。