我可以将Snapchat SDK(SnapKit)与SwiftUI一起使用吗?

我可以将Snapchat SDK(SnapKit)与SwiftUI一起使用吗?,swift,swiftui,snapchat,uiviewrepresentable,Swift,Swiftui,Snapchat,Uiviewrepresentable,我正在尝试与iOS应用程序集成,但我想使用SwiftUI而不是UIKit。我已经使用Snapkit完成了所需的设置,现在我正在尝试让snapchat登录按钮显示在我的应用程序中。我知道Snapkit SDK是为UIKit而不是SwiftUI设计的,但SwiftUI有办法使用UIViewRepresentable协议。我已经尝试过实现这个功能,但是登录按钮仍然不显示 这是我的密码: import SwiftUI import UIKit import SCSDKLoginKit struct C

我正在尝试与iOS应用程序集成,但我想使用SwiftUI而不是UIKit。我已经使用Snapkit完成了所需的设置,现在我正在尝试让snapchat登录按钮显示在我的应用程序中。我知道Snapkit SDK是为UIKit而不是SwiftUI设计的,但SwiftUI有办法使用UIViewRepresentable协议。我已经尝试过实现这个功能,但是登录按钮仍然不显示

这是我的密码:

import SwiftUI
import UIKit
import SCSDKLoginKit

struct ContentView: View {
    var body: some View {
        SnapchatLoginButtonView()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


struct SnapchatLoginButtonView: UIViewRepresentable {

    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    func makeUIView(context: Context) -> SCSDKLoginButton {
        let s = SCSDKLoginButton()
        s.delegate = context.coordinator

        return s
    }

    func updateUIView(_ uiView: SCSDKLoginButton, context: Context) {
    }

    class Coordinator: NSObject, SCSDKLoginButtonDelegate {
        func loginButtonDidTap() {
        }
    }
}
我有一种感觉,我在SCSDKLoginButton中遗漏了一些东西,但不确定它是什么,所以这里有一个文件SCSDKLoginButton.h供参考。任何帮助都将不胜感激

//
//  SCSDKLoginButton.h
//  SCSDKLoginKit
//
//  Copyright © 2018 Snap, Inc. All rights reserved.
//

#import <UIKit/UIKit.h>

@protocol SCSDKLoginButtonDelegate
- (void)loginButtonDidTap;
@end

@interface SCSDKLoginButton : UIView

@property (nonatomic, weak, nullable) id<SCSDKLoginButtonDelegate> delegate;

- (instancetype)initWithCompletion:(nullable void (^)(BOOL success, NSError *error))completion NS_DESIGNATED_INITIALIZER;

@end
//
//SCSDKLoginButton.h
//SCSDKLoginKit
//
//版权所有©2018 Snap,Inc.保留所有权利。
//
#进口
@协议scsdkloginbuttonelegate
-(无效)登录Buttondidtap;
@结束
@接口SCSDKLoginButton:UIView
@属性(非原子、弱、可空)id委托;
-(instancetype)initWithCompletion:(可为空的void(^)(BOOL success,NSError*error))完成指定的初始值设定项;
@结束

巧合的是,在您发布您的问题大约3天后,我尝试在一个独家SwiftUI/iOS13项目中实现SnapKit SDK

不幸的是,我无法直接解决您的问题,因为Snapchat在适合使用iOS 13中引入的SceneDelegate&AppDelegate范例进行开发之前,必须通过其SDK解决一些关键问题。但我希望我能阐明你的问题,并将我的发现告诉其他处于类似困境的人

以下是我在寻求在SwiftUI中实现SCSDKLoginKit和SCSDKBitmojiKit时提出的以下问题/意见:

  • 最基本的问题是,正如您正确认识到的,SCSDKLoginKit模块已经过时。SCSDKLoginClient.login()要求调用视图符合(UIKIT)UIViewController类。因此,我们必须使用具有UIViewControllerRepresentable的变通方法来充当我们的SwiftUI UIKit中介

  • 但是,根本问题在于SnapKit SDK文档尚未更新,无法为开发人员提供Snapchat Auth和应用程序逻辑之间的SceneDelegate链接。因此,即使您正确地实现了SCSDKLoginButton,也不是一帆风顺的

现在,为了直接回答您的问题,您正在尝试将SCSDKLoginButton封装在UIViewControllerRepresentable中,这是可以做到的,我相信比我更了解协调员等的人可以帮助您做到这一点。然而,我只是想表明,在snapchat提供更新的SDK之前,您目前的努力可能是徒劳的

以下是我的设置:

[ContentView.swift]

import SwiftUI

struct ContentView: View {
    @State private var isPresented = false

    var body: some View {

        Button("Snapchat Login Button") { self.isPresented = true} 
            .sheet(isPresented: $isPresented) {
                  LoginCVWrapper()
        }
    }
}
import SwiftUI
import UIKit
import SCSDKLoginKit

struct LoginCVWrapper: UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> UIViewController {
        return LoginViewController()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        //Unused in demonstration
    }
}
import UIKit
import SCSDKLoginKit

class LoginViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        performLogin() //Attempt Snap Login Here
    }

    //Snapchat Credential Retrieval Fails Here
    private func performLogin() {
        //SCSDKLoginClient.login() never completes once scene becomes active again after Snapchat redirect back to this app.
        SCSDKLoginClient.login(from: self, completion: { success, error in
            if let error = error {
                print("***ERROR LOC: manualTrigger() \(error.localizedDescription)***")
                return
            }
            if success {
                self.fetchSnapUserInfo({ (userEntity, error) in
                    print("***SUCCESS LOC: manualTrigger()***")
                    if let userEntity = userEntity {
                        DispatchQueue.main.async {
                            print("SUCCESS:\(userEntity)")
                        }
                    }
                })
            }
        })
    }

    private func fetchSnapUserInfo(_ completion: @escaping ((UserEntity?, Error?) -> ())){
        let graphQLQuery = "{me{displayName, bitmoji{avatar}}}"
        SCSDKLoginClient
            .fetchUserData(
                withQuery: graphQLQuery,
                variables: nil,
                success: { userInfo in

                    if let userInfo = userInfo,
                        let data = try? JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted),
                        let userEntity = try? JSONDecoder().decode(UserEntity.self, from: data) {
                        completion(userEntity, nil)
                    }
            }) { (error, isUserLoggedOut) in
                completion(nil, error)
        }
    }
}
[LoginCVWrapper.swift]

import SwiftUI

struct ContentView: View {
    @State private var isPresented = false

    var body: some View {

        Button("Snapchat Login Button") { self.isPresented = true} 
            .sheet(isPresented: $isPresented) {
                  LoginCVWrapper()
        }
    }
}
import SwiftUI
import UIKit
import SCSDKLoginKit

struct LoginCVWrapper: UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> UIViewController {
        return LoginViewController()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        //Unused in demonstration
    }
}
import UIKit
import SCSDKLoginKit

class LoginViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        performLogin() //Attempt Snap Login Here
    }

    //Snapchat Credential Retrieval Fails Here
    private func performLogin() {
        //SCSDKLoginClient.login() never completes once scene becomes active again after Snapchat redirect back to this app.
        SCSDKLoginClient.login(from: self, completion: { success, error in
            if let error = error {
                print("***ERROR LOC: manualTrigger() \(error.localizedDescription)***")
                return
            }
            if success {
                self.fetchSnapUserInfo({ (userEntity, error) in
                    print("***SUCCESS LOC: manualTrigger()***")
                    if let userEntity = userEntity {
                        DispatchQueue.main.async {
                            print("SUCCESS:\(userEntity)")
                        }
                    }
                })
            }
        })
    }

    private func fetchSnapUserInfo(_ completion: @escaping ((UserEntity?, Error?) -> ())){
        let graphQLQuery = "{me{displayName, bitmoji{avatar}}}"
        SCSDKLoginClient
            .fetchUserData(
                withQuery: graphQLQuery,
                variables: nil,
                success: { userInfo in

                    if let userInfo = userInfo,
                        let data = try? JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted),
                        let userEntity = try? JSONDecoder().decode(UserEntity.self, from: data) {
                        completion(userEntity, nil)
                    }
            }) { (error, isUserLoggedOut) in
                completion(nil, error)
        }
    }
}
[LoginViewController.swift]

import SwiftUI

struct ContentView: View {
    @State private var isPresented = false

    var body: some View {

        Button("Snapchat Login Button") { self.isPresented = true} 
            .sheet(isPresented: $isPresented) {
                  LoginCVWrapper()
        }
    }
}
import SwiftUI
import UIKit
import SCSDKLoginKit

struct LoginCVWrapper: UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> UIViewController {
        return LoginViewController()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        //Unused in demonstration
    }
}
import UIKit
import SCSDKLoginKit

class LoginViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        performLogin() //Attempt Snap Login Here
    }

    //Snapchat Credential Retrieval Fails Here
    private func performLogin() {
        //SCSDKLoginClient.login() never completes once scene becomes active again after Snapchat redirect back to this app.
        SCSDKLoginClient.login(from: self, completion: { success, error in
            if let error = error {
                print("***ERROR LOC: manualTrigger() \(error.localizedDescription)***")
                return
            }
            if success {
                self.fetchSnapUserInfo({ (userEntity, error) in
                    print("***SUCCESS LOC: manualTrigger()***")
                    if let userEntity = userEntity {
                        DispatchQueue.main.async {
                            print("SUCCESS:\(userEntity)")
                        }
                    }
                })
            }
        })
    }

    private func fetchSnapUserInfo(_ completion: @escaping ((UserEntity?, Error?) -> ())){
        let graphQLQuery = "{me{displayName, bitmoji{avatar}}}"
        SCSDKLoginClient
            .fetchUserData(
                withQuery: graphQLQuery,
                variables: nil,
                success: { userInfo in

                    if let userInfo = userInfo,
                        let data = try? JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted),
                        let userEntity = try? JSONDecoder().decode(UserEntity.self, from: data) {
                        completion(userEntity, nil)
                    }
            }) { (error, isUserLoggedOut) in
                completion(nil, error)
        }
    }
}
[运行如下]

有关SceneDelegate接口链接问题的更多信息: 当您不可避免地实现SCSDKLoginClient.login()调用时(可能是在按下SCSDKLoginButton时),Snapchat将打开,它将正确显示“授权访问”表,假设您的应用程序链接到Snapchat开发门户中

接受这些权限后,Snapchat将重定向到应用程序。但是,这就是应用程序与检索snapchat用户名/bitmoji之间的链接将崩溃的地方。这是因为在新的iOS 13应用程序中,SceneDelegate会在应用程序状态更改时进行处理,而不是像iOS 13之前的版本那样处理AppDelegate。因此,Snapchat返回用户数据,但您的应用程序从未检索到它

[前进]

  • SnapKitSDK(当前版本为1.4.3)需要与文档一起更新
  • 我刚刚向Snapchat提交了一个支持问题,询问此更新何时发布,因此如果听到更多信息,我将更新此内容。抱歉如果您正在寻找SCSDKLoginButton()问题的直接解决方案,我只是想让您知道在当前时刻,除此之外还有哪些挑战
[进一步阅读]

import SwiftUI

struct ContentView: View {
    @State private var isPresented = false

    var body: some View {

        Button("Snapchat Login Button") { self.isPresented = true} 
            .sheet(isPresented: $isPresented) {
                  LoginCVWrapper()
        }
    }
}
import SwiftUI
import UIKit
import SCSDKLoginKit

struct LoginCVWrapper: UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> UIViewController {
        return LoginViewController()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        //Unused in demonstration
    }
}
import UIKit
import SCSDKLoginKit

class LoginViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        performLogin() //Attempt Snap Login Here
    }

    //Snapchat Credential Retrieval Fails Here
    private func performLogin() {
        //SCSDKLoginClient.login() never completes once scene becomes active again after Snapchat redirect back to this app.
        SCSDKLoginClient.login(from: self, completion: { success, error in
            if let error = error {
                print("***ERROR LOC: manualTrigger() \(error.localizedDescription)***")
                return
            }
            if success {
                self.fetchSnapUserInfo({ (userEntity, error) in
                    print("***SUCCESS LOC: manualTrigger()***")
                    if let userEntity = userEntity {
                        DispatchQueue.main.async {
                            print("SUCCESS:\(userEntity)")
                        }
                    }
                })
            }
        })
    }

    private func fetchSnapUserInfo(_ completion: @escaping ((UserEntity?, Error?) -> ())){
        let graphQLQuery = "{me{displayName, bitmoji{avatar}}}"
        SCSDKLoginClient
            .fetchUserData(
                withQuery: graphQLQuery,
                variables: nil,
                success: { userInfo in

                    if let userInfo = userInfo,
                        let data = try? JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted),
                        let userEntity = try? JSONDecoder().decode(UserEntity.self, from: data) {
                        completion(userEntity, nil)
                    }
            }) { (error, isUserLoggedOut) in
                completion(nil, error)
        }
    }
}
  • Facebook已经更新了他们的工具和文档,以纳入场景/应用程序代理。请参见此处的步骤“5.连接应用程序代理和场景代理”:

    • @Stephen2697正确地指出,由于SceneDelegate现在处理的是oauth重定向,而不是AppDelegate,因此snap sdk尚未为iOS 13构建。我找到了一种解决方法,可以在场景委托中使用SCSDKLoginClient.application()方法(为appdelegate制作)。以下是代码,将其添加到场景代理中,传递到Snapchat登录的完成处理程序将运行:

      func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
          for urlContext in URLContexts {
              let url = urlContext.url
              var options: [UIApplication.OpenURLOptionsKey : Any] = [:]
              options[.openInPlace] = urlContext.options.openInPlace
              options[.sourceApplication] = urlContext.options.sourceApplication
              options[.annotation] = urlContext.options.annotation
              SCSDKLoginClient.application(UIApplication.shared, open: url, options: options)
          }
      }
      

      谢谢,当我询问snap开发者支持时,他们告诉我他们“计划在将来集成swiftui”,但我不知道这实际上意味着什么。我想我现在就走uikit路线。即使uikit有一个场景代理文件,所以如果您使用的是XCode 11,SwiftUI和uikit都不会立即工作。@Stephen2697我想我已经找到了使用scenedelegate的解决方法。检查我的更新解决方案BZE12此功能的具体用途是什么?我把它放在我的App.swift文件中,因为从最新的iOS Xcode版本开始,不再使用代理。我在func场景中有一个断点,但当我返回到我的应用程序时,它并没有被击中。任何帮助都将不胜感激!我完全不知道它会在App.swift中出现在哪里,但ios 14肯定仍然支持代理。您只需在创建项目时选择它,当前如果您使用swiftui,它默认为App.swift