[iOS13] ASWebAuthenticationSession用いたOAuth認証の変更点

# はじめに
OAuth認証やASWebAuthenticationSession、などについてはここではふれません。
ASWebAuthenticationSession用いたOAuth認証をiOS12で実装しており、これからiOS13に対応する方法の一例を書きたいと思います。

※ `UIApplication.sharedApplication.keyWindow;` が iOS13 で DEPRECATED になってしまったので、別の方法に書き換えました。

# 変更点

- `ASWebAuthenticationSession` の `start` を呼び出す前に、 `presentationContextProvider` を設定しなければいけない

> In macOS, and for iOS apps with a deployment target of iOS 13 or later, after you call start, the session instance stores a strong reference to itself. To avoid deallocation during the authentication process, the session keeps the reference until after it calls the completion handler.

> For iOS apps with a deployment target earlier than iOS 13, your app must keep a strong reference to the session to prevent the system from deallocating the session while waiting for authentication to complete.

[参照元](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/2990953-start?language=objc)

これにより、以下の書き換えが必要になります。

```objective_c:iOS12.m
self.webAuthenticationSession = [[ASWebAuthenticationSession alloc] initWithURL:[NSURL URLWithString:url] callbackURLScheme:nil completionHandler:^(NSURL *callbackURL, NSError *error) {
    // SessionToken の取得など
    self.webAuthenticationSession = nil;
}];
[self.webAuthenticationSession start];
```

↓

```objective_c:iOS13.m
self.webAuthenticationSession = [[ASWebAuthenticationSession alloc] initWithURL:[NSURL URLWithString:url] callbackURLScheme:nil completionHandler:^(NSURL *callbackURL, NSError *error) {
    // SessionToken の取得など
    self.webAuthenticationSession = nil;
}];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13.0, *)) {
    self.webAuthenticationSession.presentationContextProvider = uiViewController; // uiViewControllerについては後で記載
}
#endif
[self.webAuthenticationSession start];
```

以下のように `presentationContextProvider` に `ASWebAuthenticationPresentationContextProviding` Protocol を実装したインスタンスを設定すればいいだけです。

```objective_c
self.webAuthenticationSession.presentationContextProvider = uiViewController;
```

例えば


```objective_c:HogeLoginViewController.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000

API_AVAILABLE(ios(13.0), macos(10.15))
@interface HogeLoginViewController : UIViewController <ASWebAuthenticationPresentationContextProviding>

@end
#endif
```

```objective_c:HogeLoginViewController.m
#import "HogeLoginViewController.h"

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000

@implementation HogeLoginViewController {

}

...

- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
    return view.window;
}
...

@end

#endif
```

# 問題点

これだと、認証システム基盤を作成するには、問題があります。
APIの引数にUI要素のものを渡し他場合、以下のような問題が出てくるからです。

- Androidと共通基盤の場合、引数が異なる
- 引数にUI要素なものを渡さないといけない？
- Unityなどで利用する場合、どうするの？

解決策は簡単で、 `UIViewController` を使わないだけです。

# 解決策

```objective_c:HogeBridgeAPI.h
#import <Foundation/Foundation.h>

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000

API_AVAILABLE(ios(13.0), macos(10.15))
@interface HogeBridgeAPI : NSObject <ASWebAuthenticationPresentationContextProviding>

@end
#endif
```

```objective_c:HogeBridgeAPI.m
#import "HogeBridgeAPI.h"

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000

@implementation HogeBridgeAPI {

}

- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
    for (UIScene *uiScene in UIApplication.sharedApplication.connectedScenes) {
        if (uiScene.activationState != UISceneActivationStateForegroundActive) {
            continue;
        }
        if ([uiScene isKindOfClass:UIWindowScene.class]) {
            UIWindowScene *uiWindowScene = (UIWindowScene *)uiScene;
            for (UIWindow *uiWindow in uiWindowScene.windows) {
                if(uiWindow.isKeyWindow){
                    return uiWindow;
                }
            }
        }
    }
    return nil;
}

@end
#endif
```

HogeBridgeAPIなるものを作成します。

```objective_c
for (UIScene *uiScene in UIApplication.sharedApplication.connectedScenes) {
    if (uiScene.activationState != UISceneActivationStateForegroundActive) {
        continue;
    }
    if ([uiScene isKindOfClass:UIWindowScene.class]) {
        UIWindowScene *uiWindowScene = (UIWindowScene *)uiScene;
        for (UIWindow *uiWindow in uiWindowScene.windows) {
            if(uiWindow.isKeyWindow){
                return uiWindow;
            }
        }
    }
}
```

`presentationAnchorForWebAuthenticationSession` の戻り値は、上記とすることで `UIViewController` を使わくても大丈夫です。

あとは、 コンストラクタなどで `HogeBridgeAPI` を生成

```objective_c
self.webAuthenticationSession.presentationContextProvider = self.bridgeAPI;
```

などしてあげれば解決します。