UnityからAR Foundation経由でAR Kitを利用したとき、Application.RequestUserAuthorizationが動かない

仕事でARアプリケーションを作っていたのだが、 `Application.RequestUserAuthorization` が動作していない。
仕様上、画面をタッチしてアプリをスタート！ みたいな画面で、権限の要求 & 拒否されたのであれば権限が必要な旨を通知したい。さて…。

## 原因は何か
Debug.Logを大量に貼り、どこまで処理が動いていて、どこまで処理が動いていないかを探っていく。
直前の権限確認と、分岐までは動いているので ~~どうやら `Application.HasUserAuthorization` は動作しているようだ。~~ 動いてなかった。`Application.HasUserAuthorization` に失敗したとき常に `false` を返すようになっているだけだったようだ。

ということは、C#スクリプトに問題があって私の書いた処理が動いていないわけではなく、なぜかわからないが `Application.RequestUserAuthorization` だけが限定的に処理をスキップされているようだ。

## iOS用にコンバートされた処理を追っていく
ビルドされた`xcodeproj`ファイルを開き、中を見る。
自動生成された難読なコードが大量に出てきてげんなりするが、Debug.Logを大量に貼っていたのが功を奏した。
XCodeで `ababababab` など、`Application.RequestUserAuthorization` を呼び出している部分の前後に置いた文字列で検索しつつ、なんとかコードを追っていくと、 `UnityRequestAVCapturePermission` という関数にたどり着いた。

## #if !PLATFORM_TVOS && (UNITY_USES_WEBCAM || UNITY_USES_MICROPHONE)

たどりついたのは、 `Classes > Unity`にある`AVCapture.mm`というふぁいるにある`UnityRequestAVCapturePermission`という関数だった。
関数名で察しはつくが、どうやらUnity上で呼ばれた`Application.RequestUserAuthorization`などのObj-C側の実態っぽい。
中身はこんな感じだ

```Objective-C:AVCapture.mm
#include "AVCapture.h"

#include <AVFoundation/AVFoundation.h>


static NSString* MediaTypeFromEnum(int captureType)
{
    if (captureType == avAudioCapture)
        return AVMediaTypeAudio;
    else if (captureType == avVideoCapture)
        return AVMediaTypeVideo;
    return nil;
}

extern "C" int UnityGetAVCapturePermission(int captureType)
{
    NSString* mediaType = MediaTypeFromEnum(captureType);
    if (mediaType == nil)
        return avCapturePermissionDenied;

#if !PLATFORM_TVOS && (UNITY_USES_WEBCAM || UNITY_USES_MICROPHONE)
    NSInteger status = AVAuthorizationStatusAuthorized;
    status = [AVCaptureDevice authorizationStatusForMediaType: mediaType];

    if (status == AVAuthorizationStatusNotDetermined)
        return avCapturePermissionUnknown;
    else if (status == AVAuthorizationStatusAuthorized)
        return avCapturePermissionGranted;
#endif

    return avCapturePermissionDenied;
}

extern "C" void UnityRequestAVCapturePermission(int captureType)
{
#if !PLATFORM_TVOS && (UNITY_USES_WEBCAM || UNITY_USES_MICROPHONE)
    NSString* mediaType = MediaTypeFromEnum(captureType);
    if (mediaType == nil)
        return;

    [AVCaptureDevice requestAccessForMediaType: mediaType completionHandler:^(BOOL granted) {
        UnityReportAVCapturePermission();
    }];
#endif
}
```

気になるのは `#if !PLATFORM_TVOS && (UNITY_USES_WEBCAM || UNITY_USES_MICROPHONE)` のあたり。というかこれしかない。絶対これだ。
調べてみると以下のようなスレッドがあった https://forum.unity.com/threads/ios-app-never-asks-for-microphone-permissions.450307/ 要約すると、

+ `Application.RequestUserAuthorization`が上手く動いてない
+ どうやら `UNITY_USES_MICROPHONE` が定義されていないっぽい

ということのようだ。
上記のスレッドは2017年の物なのだが、なぜか同じような問題が再発しているようだ。

（噂によるとUnityからARKitを使うとこの辺をARKitが握ってしまうのでうまく動作しなくなるとの事だが、詳細は不明）

## 解決方法
解決方法は簡単、というかゴリラ並みの力技だが、以下のように修正するだけだ


```Objective-C:AVCapture.mm
#include "AVCapture.h"

#include <AVFoundation/AVFoundation.h>


static NSString* MediaTypeFromEnum(int captureType)
{
    if (captureType == avAudioCapture)
        return AVMediaTypeAudio;
    else if (captureType == avVideoCapture)
        return AVMediaTypeVideo;
    return nil;
}

extern "C" int UnityGetAVCapturePermission(int captureType)
{
    NSString* mediaType = MediaTypeFromEnum(captureType);
    if (mediaType == nil)
        return avCapturePermissionDenied;

#if !PLATFORM_TVOS
    NSInteger status = AVAuthorizationStatusAuthorized;
    status = [AVCaptureDevice authorizationStatusForMediaType: mediaType];

    if (status == AVAuthorizationStatusNotDetermined)
        return avCapturePermissionUnknown;
    else if (status == AVAuthorizationStatusAuthorized)
        return avCapturePermissionGranted;
#endif

    return avCapturePermissionDenied;
}

extern "C" void UnityRequestAVCapturePermission(int captureType)
{
#if !PLATFORM_TVOS
    NSString* mediaType = MediaTypeFromEnum(captureType);
    if (mediaType == nil)
        return;

    [AVCaptureDevice requestAccessForMediaType: mediaType completionHandler:^(BOOL granted) {
        UnityReportAVCapturePermission();
    }];
#endif
}
```

何をしたかというと、  `#if !PLATFORM_TVOS && (UNITY_USES_WEBCAM || UNITY_USES_MICROPHONE)` を `#if !PLATFORM_TVOS` と修正した。

`UNITY_USES_MICROPHONE` および `UNITY_USES_WEBCAM` はUnity側で `Microphone Usage Description`, `Camera Usage Description` を設定していないときの安全装置（設定してないと権限要求した時点でアプリが落ちる）らしいのだが、もちろんDescriptionは設定してあるので、とりあえずなくなっても大丈夫だろう。

場当たり的な対処しかできないのが歯がゆいが、差し当たって問題なくアプリは動いている。


気になるのはなんでこんなことになるのかという原因の部分だが、ここまで探るので結構疲れたのでそれはまた今度…。