【SwiftUI】ASWebAuthenticationSessionを使用して、Webの認証セッションを行う

# 何をするのか？
iOSから各Webサービスの認証機能（Twitter連携など）を利用するためには、ASWebAuthenticationSessionを使用しWebの認証セッションを作成し、セッションの表示アンカーとなるWindowを指定して、表示アンカーでの認証操作後、コールバックを受け取る必要があります。
今回はその一連の処理をSwiftUIで実装する手段を紹介します。
また本記事ではWebサービスの認証例としてライブラリSwifterを用いた、Twitterの連携処理をベースに記述します。他のWebサービスの認証処理を実装する際も、基本的には表示アンカーを指定し、コールバックを受け取る処理は変わらないはずなので参考にしていただければ幸いです。

　**使用ライブラリ**
- Swifter
https://github.com/mattdonnelly/Swifter

# ASWebAuthenticationSessionを使用した、Webの認証セッション
## 表示アンカーの定義
まずは認証セッションの表示アンカーをシングルトンで参照できるように以下のように記述します。
シングルトンで定義する理由は、SceneDelegateでアンカーの設定を行い、その設定したアンカーを別の箇所で利用するためです。
```swift
class Singleton: ObservableObject {
    static let shared = Singleton()
    var globalPresentationAnchor: ASPresentationAnchor?

    private init() { }
}
```

## 表示アンカーの設定
SceneDelegateでアンカーにwindowを設定することで、アンカーをアプリのルートWindowに設定します。
```swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    @StateObject var singleton = Singleton.shared
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        singleton.globalPresentationAnchor = window
    }
}
```

## 表示アンカーを返すViewControllerの定義
次にASWebAuthenticationSessionが作成された時に表示アンカーを返すViewControllerを定義します。
```swift
class TwitterViewController: UIViewController, ASWebAuthenticationPresentationContextProviding {
    var anchor: ASPresentationAnchor

    init(anchor: ASPresentationAnchor) {
        self.anchor = anchor
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        return anchor
    }
}
```

## 認証処理
以下がSwifterを用いた認証処理になります。表示アンカーを返すTwitterViewControllerをProviderに設定することで、認証時にTwitterの認証ページをTwitterViewControllerに表示することができます。認証後コールバックとして、アクセストークンを受け取り、これを利用することでTwitterApiの各種機能を利用できます。
その他のパラメータなどTwitterApiに関することはここでは説明省略いたします。
```swift
class TwitterManager {
    public var vc: TwitterViewController

    init(vc: TwitterViewController) {
        self.vc = vc
    }

    func executeTwitterAuth() {
        Swifter(consumerKey: "TwitterConsumerKey", consumerSecret: "TwitterConsumerSecretKey").authorize(withProvider: vc, ephemeralSession: true, callbackURL: URL(string: "CallBackUrl")!, forceLogin: true) { (token, _) in
            guard let accessToken = token else { return }
            UserDefaultsManager().setTwitterKey(key: accessToken.key); UserDefaultsManager().setTwitterSecretKey(secretKey: accessToken.secret)
        } failure: { (error) in
            print(error)
        }
    }
}
```
## 認証処理発火
後はButtonなどで認証処理を発火すれば実装完了です。

```swift
Button {
    TwitterManager(vc: TwitterViewController(anchor: singleton.globalPresentationAnchor ?? ASPresentationAnchor())).executeTwitterAuth()
} label: {
}
```

# おわりに
今回はTwitterを例にSwiftUIでWebの認証セッション行う方法を紹介しました。
普段Swiftで実装していると記事も多く詳しい仕組みをあまり理解せずにでも実装できることが多いので、実装したことある機能でもSwiftUIで実装することで再度ドキュメントを読み返す必要があったりで、理解が深まりますね。
コード内容、説明など不十分な点、誤っている点などあればコメントいただければ幸いです。
より詳しくASWebAuthenticationSessionの仕様など詳しく知りたい方は参考リンクを参照ください。
本記事を閲覧いただきありがとうございました！

# 参考
https://developer.apple.com/jp/documentation/authenticationservices/authenticating_a_user_through_a_web_service/
https://stackoverflow.com/questions/58574143/is-it-possible-to-use-aswebauthenticationsession-from-swiftui
https://github.com/mattdonnelly/Swifter