12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOS13(beta)でASWebAuthenticationSessionの変更点

Posted at

注:本記事は Xcode 11 beta 6 (2019/08/20)時点での変更内容での話になります

iOS13でログイン画面が開かない

開発中のアプリでASWebAuthenticationSessionを利用してOAuthログインする機能があり、iOS13(beta)で動作確認をしたところログインボタンを押してログイン画面を出そうとしても出てくれない状態でした。

ログを見てみると以下のエラーの出力がありました。

Cannot start ASWebAuthenticationSession without providing presentation context. Set presentationContextProvider before calling -start.

どうやらiOS13からはstart()を呼び出す前にpresentationContextProviderをセットする必要があるようです。

ASWebAuthenticationPresentationContextProviding

presentationContextProviderASWebAuthenticationPresentationContextProviding(document)というprotocolに適合したNSObjectのサブクラスのインスタンスのようです。

定義は以下のようになっています。

/** @abstract Provides context to target where in an application's UI the authorization view should be shown.
 */
@available(iOS 13.0, *)
public protocol ASWebAuthenticationPresentationContextProviding : NSObjectProtocol {

    
    /** @abstract Return the ASPresentationAnchor in the closest proximity to where a user interacted with your app to trigger
     authentication. If starting an ASWebAuthenticationSession on first launch, use the application's main window.
     @param session The session requesting a presentation anchor.
     @result The ASPresentationAnchor most closely associated with the UI used to trigger authentication.
     */
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor
}

ASPresentationAnchor

なるほど、そのprotocolのインスタンスを用意すればいいことはわかったけど、ASPresentationAnchorってなんだ?と思ったらUIWindow(iOS, Mac Catalyst, tvOS) or NSWindow(macOS)のtypealiasでした。

直してみる

シンプルに書くと多分こんな感じに実装すれば良さそう

import AuthenticationServices
import UIKit

class ViewController: UIViewController {

    private var authenticationSession: ASWebAuthenticationSession?

    // ログインボタンを押した後に呼ばれる
    func loginStart() {
        let authenticationSession = ASWebAuthenticationSession(
            url: URL(string: "https://...")!,
            callbackURLScheme: "<url-scheme>",
            completionHandler: loginCompletionHandler(url:error:))
        if #available(iOS 13.0, *) {
            // 
            authenticationSession.presentationContextProvider = self
        }
        authenticationSession.start()

        // authenticationSessionが解放されないようにプロパティに入れておく
        self.authenticationSession = authenticationSession
    }

    // ログインの結果のハンドリング
    func loginCompletionHandler(url: URL?, error: Error?) {
        authenticationSession = nil
        ...
    }

    ...
}

@available(iOS 13.0, *)
extension ViewController: ASWebAuthenticationPresentationContextProviding {

    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
            return view.window!
    }
}

ちなみに今開発中のアプリではiOS11も対応しており、ASWebAuthenticationSessionSFAuthenticationSessionをラップしたクラスを定義していたため、ASWebAuthenticationPresentationContextProvidingを適合させたprivateなクラスを用意して実装しました。
このように実装する場合、presentationContextProviderは弱参照なので解放されてしまいます。そのためASWebAuthenticationPresentationContextProvidingを適合したオブジェクトのインスタンスもプロパティとして保持しないといけないことに注意してください。

class AuthenticationSession {

    ...
    private var authenticationSession: Any?
    private var presentationContextProvider: Any?

    func loginStart(from viewController: UIViewController) {
        if #available(iOS 12.0, *) {
            let authenticationSession = ASWebAuthenticationSession(
                url: loginURL,
                callbackURLScheme: "<url-scheme>",
                completionHandler: loginCompletionHandler(url:error:))
            if #available(iOS 13.0, *) {
                let presentationContextProvider = AuthPresentationContextProver(viewController: viewController)
                authenticationSession.presentationContextProvider = presentationContextProvider
                self.presentationContextProvider = presentationContextProvider
            }
            authenticationSession.start()
            self.authenticationSession = authenticationSession
        } else { // iOS 11.x
            let authenticationSession = SFAuthenticationSession(
                url: loginURL,
                callbackURLScheme: "<url-scheme>",
                completionHandler: loginCompletionHandler(url:error:))
            authenticationSession.start()
            self.authenticationSession = authenticationSession
        }
    }

    func loginCompletionHandler(url: URL?, error: Error?) {
        authenticationSession = nil
        presentationContextProvider = nil
        ...
    }

    ...

    @available(iOS 13.0, *)
    private class AuthPresentationContextProver: NSObject, ASWebAuthenticationPresentationContextProviding {

        private weak var viewController: UIViewController!

        init(viewController: UIViewController) {
            self.viewController = viewController
            super.init()
        }

        func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
            return viewController?.view.window!
        }
    }
}

おわり

ASWebAuthenticationSessionを使っているアプリはiOS13が出るまでに対応しないとログインが出来なくなってしまう可能性があるので注意しましょう。

12
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?