2023/11/28 追記
- しばらく気がつきませんでしたが、SwiftUIでASWebAuthenticationSessionを利用するAPIが追加されていました
- 利用可能なOSは限られていますが、可能であればこちらのAPIを使う方が良いでしょう
webAuthenticationSession
- 本家ドキュメント
- iOS16.4~
-
@Environment
で利用する - 扱うのはASWebAuthenticationSessionではなく、
WebAuthenticationSession
利用方法
- SwiftUI用にチューニングされていて、Concurrency対応していたり既存の方法よりかなり使い勝手が良くなっています
- 以下はApple公式の例をちょっと変えたものです
struct WebAuthenticationSessionExample: View {
@Environment(\.webAuthenticationSession) private var webAuthenticationSession
var body: some View {
Button("Sign In") {
Task {
do {
// authenticateメソッドの戻り値でコールバックURLをもらえる
let urlWithToken = try await webAuthenticationSession.authenticate(
using: URL(string: "https://www.example.com")!,
callbackURLScheme: "x-example-app"
)
// URLComponentsで必要なパラメータを取り出す
let queryItems = URLComponents(string: urlWithToken.absoluteString)?.queryItems
// OAuth2だったらここからトークン交換をしたりする
} catch {
print(error)
}
}
}
}
}
ここから追記前記事
導入
新卒でSwiftはじめて3ヶ月が経ちました。あっという間です…
今回はSwiftUIでTwitter連携させたかったとき悩んだところが解消できたのでせっかくなので記事にしたいと思います。エンジニアっぽくなってきて楽しいです
したかったこと
-
SwiftUI
のViewで作ったボタンをポチったらASWebAuthenticationSession
でTwitterとか連携するあの画面が出てきてサインインしたら閉じるやつが作りたい(語彙力)
できたやつ
AuthPresentationContextProver クラスはこちらの記事から拝借しました。ありがとうございます
import SwiftUI
import AuthenticationServices
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! ?? ASPresentationAnchor()
}
}
struct RenkeiButton: View {
@State var session: ASWebAuthenticationSession?
@State var presentationContextProvider: AuthPresentationContextProver?
// リクエストトークンもらうマン
func postOAuthRequestToken(success: 成功した時のHandler, failure: 失敗した時)
// リクエストトークンから連携画面表示するマン
func authStart(requestToken: とーくん) {
let queryUrl = tokenで作る
let callbackURLScheme = "ちょめちょめ"
self.session = ASWebAuthenticationSession(url: queryUrl, callbackURLScheme: callbackURLScheme, completionHandler: { (url, error) in
// callbackURLScheme + parameters のURLが帰ってくるのでちょめちょめする
// キャンセルの時はエラーになる
})
let presentationContextProvider = AuthPresentationContextProver(viewController: UIHostingController(rootView: self))
self.session?.presentationContextProvider = presentationContextProvider
self.presentationContextProvider = presentationContextProvider
self.session?.start()
}
var body: some View {
Button(action: {
self.postOAuthRequestToken(
success: { (requestToken, response) in
self.authStart(token: requestToken)
}, failure: { (error) in
})
}, label: {
Text("連携や!!")
})
}
}
悩んでたとこ
iOS13ではpresentationContextProvider
にASWebAuthenticationPresentationContextProviding
とかいうProtocolを満たすNSObjectを設定をしていないと連携画面のモーダルが出ません…
Controllerベースで作れば簡単にできるけど…ってなってた時に@simorgh3196さんの記事を発見して、もしやUIHostingControllerで渡してやれば同じようにできる…?となってうまくいった次第です
UIHostingControllerで渡してやらなくても…
要は現在のwindowが知りたいので@shizさんのこちらの記事を参考に…
class AuthPresentationContextProver: NSObject, ASWebAuthenticationPresentationContextProviding {
@Environment(\.window) var window
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
return window ?? ASPresentationAnchor()
}
}
とすることもできます。(この書き方いいんでしょうか…)
おわりに
SwiftUI、iOS13 まだまだBeta段階ですのでこの書き方もいずれ使えなくなるかもなので注意です!
SwiftUIたのしい!