Использование ASWebAuthentication в SwiftUI - PullRequest
0 голосов
/ 10 февраля 2020

Возникли проблемы с установлением подлинности для работы из представления SwiftUI. Я использую ASWebAuthentication, и при каждом запуске я получаю сообщение об ошибке:

Невозможно запустить ASWebAuthenticationSession без предоставления контекста представления. Установите presentationContextProvider перед вызовом -start.

Я создаю ViewController и передаю ссылку на окно Scene Delegate на основе этого сообщения о переполнении стека , но этот ответ не кажется, работает на меня. Я также нашел это сообщение reddit , но мне немного неясно, как им удалось инициализировать представление с помощью окна до того, как будет установлено окно делегата сцены.

Вот код, который я использую для представления SwiftUI:

import SwiftUI
import AuthenticationServices

struct Spotify: View {
  var body: some View {
    Button(action: {
        self.authWithSpotify()
    }) {
        Text("Authorize Spotify")
    }
  }

  func authWithSpotify() {

    let authUrlString = "https://accounts.spotify.com/authorize?client_id=\(spotifyID)&response_type=code&redirect_uri=http://redirectexample.com/callback&scope=user-read-private%20user-read-email"
    guard let url = URL(string: authUrlString) else { return }

    let session = ASWebAuthenticationSession(
        url: url,
        callbackURLScheme: "http://redirectexample.com/callback",
        completionHandler: { callback, error in

            guard error == nil, let success = callback else { return }

            let code = NSURLComponents(string: (success.absoluteString))?.queryItems?.filter({ $0.name == "code" }).first

            self.getSpotifyAuthToken(code)
    })

    session.presentationContextProvider = ShimViewController()
    session.start()
  }

  func getSpotifyAuthToken(_ code: URLQueryItem?) {
    // Get Token
  }

}

struct Spotify_Previews: PreviewProvider {
  static var previews: some View {
    Spotify()
  }
}

class ShimViewController: UIViewController, ASWebAuthenticationPresentationContextProviding {
  func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
    return globalPresentationAnchor ?? ASPresentationAnchor()
  }
}

А в SceneDelegate:

var globalPresentationAnchor: ASPresentationAnchor? = nil

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

    // Use a UIHostingController as window root view controller
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: Spotify())
        self.window = window
        window.makeKeyAndVisible()
    }

    globalPresentationAnchor =  window
}

Есть идеи, как мне это сделать?

Ответы [ 2 ]

0 голосов
/ 21 февраля 2020

Что касается поста Reddit, я заставил его работать как есть. Мое недоразумение заключалось в том, что AuthView не используется как представление «интерфейса». Я создал обычное представление SwiftUI для своего представления аутентификации, и у меня есть кнопка с действием, создающим экземпляр AuthView и вызывающим функцию, которая обрабатывает сеанс. Я храню globalPositionAnchor в @EnvironmentObject, но вы также должны иметь возможность использовать его из глобальной переменной. Надеюсь, это поможет!

struct SignedOutView: View {
    @EnvironmentObject var contentManager: ContentManager

    var body: some View {
        VStack {
            Text("Title")
            .font(.largeTitle)

            Spacer()

            Button(action: {AuthProviderView(window: self.contentManager.globalPresentationAnchor!).signIn()}) {
                Text("Sign In")
                    .padding()
                    .foregroundColor(.white)
                    .background(Color.orange)
                    .cornerRadius(CGFloat(5))
                    .font(.headline)
            }.padding()
        }
    }
}
0 голосов
/ 12 февраля 2020

Я сталкивался с чем-то похожим раньше при реализации ASWebAuthenticationSession. Одна вещь, которую я не осознавал, это то, что у вас должна быть сильная ссылка на переменную сеанса. Поэтому я бы сделал session переменную свойством вашего класса и посмотрел бы, исправит ли это проблему. Короткий фрагмент того, что я имею в виду:

// initialize as a property of the class
var session: ASWebAuthenticationSession?

func authWithSpotify() {
    let authUrlString = "https://accounts.spotify.com/authorize?client_id=\(spotifyID)&response_type=code&redirect_uri=http://redirectexample.com/callback&scope=user-read-private%20user-read-email"
    guard let url = URL(string: authUrlString) else { return }

    // assign session here
    session = ASWebAuthenticationSession(url: url, callbackURLScheme: "http://redirectexample.com/callback", completionHandler: { callback, error in

            guard error == nil, let success = callback else { return }

            let code = NSURLComponents(string: (success.absoluteString))?.queryItems?.filter({ $0.name == "code" }).first

            self.getSpotifyAuthToken(code)
    })

    session.presentationContextProvider = ShimViewController()
    session.start()
}
...