我知道这是一个设计问题而不是技术问题,但这听起来像是一个非常常见的问题,所以我认为它也适用于此。
我有一个iOS应用程序(用Swift编写),应该支持3种登录方法:Facebook,Google&使用电子邮件注册并注册密码。
一旦用户第一次登录,我想记住他使用的方法,并在下次应用程序启动时自动登录(假设它当然没有留在后台)。
对于Google,我可以使用以下代码:
let googleSignIn = GPPSignIn.sharedInstance()
if googleSignIn.trySilentAuthentication() {
// was able to login automatically using google
// no need to show login screen
}
此代码与UI无关,这意味着我可以从AppDelegate调用它,并提前知道是否应该显示登录屏幕或自动跳到应用程序的主屏幕。
看起来Facebook中的等效功能是让ViewController实现FBLoginViewDelegate
协议,有FBLoginView
按钮,并将自己指定为FBLoginView的委托:
class LoginViewController: UIViewController, FBLoginViewDelegate, GPPSignInDelegate {
@IBOutlet weak var fbLoginView: FBLoginView!
override func viewDidLoad() {
self.fbLoginView.delegate = self
}
...
对于Facebook自动登录,我可以在此视图控制器中使用以下代码:
func loginViewFetchedUserInfo(loginView : FBLoginView!, user: FBGraphUser) {
// logged in automatically using facebook, open main page
}
但是,此代码是UI- 依赖,这意味着我必须实例化ViewController,否则loginViewFetchedUserInfo回调不会触发,我不知道用户是否使用Facebook自动登录
当然,如果我想在用户使用电子邮件和电子邮件注册后进行自动登录密码我需要自己管理它,在这种情况下我可以做的只是使用NSUserDefaults
保存凭据。
基本上我也可以使用相同的机制来记住用户用来登录的方法,但我想这对于Facebook&谷歌我仍然应该调用他们适当的API来实际执行自动登录。
我遇到的问题是,对于Facebook而言,没有像Google这样的UI- 独立 API,而且我必须通过"登录界面,虽然我最终可能不需要它。
另一个问题是Facebook事件是异步触发的,所以我需要"暂停"登录按钮演示,直到它被触发,虽然我可以很容易地知道用户已经登录过Facebook,通过使用NSUserDefaults保持指示,就像我注册的情况一样,我还是想做,知道我应该使用哪个API来验证自动登录。
关于Google登录,不清楚trySilentAuthentication返回的值是否足以验证登录,或者是否还需要等待GPPSignInDelegate.finishedWithAuth
触发,因此需要实例化LoginView在这种情况下。
这转变为一个非常长的问题,但同样,这听起来像是一种非常常见的情况,所以希望我能得到一些见解。
答案 0 :(得分:0)
如果有人遇到类似的问题,我缺少的Facebook API是FBSession.openActiveSessionWithAllowLoginUI
。
这就是我的代码的样子:
class LoginHelper : NSObject, GPPSignInDelegate {
private let googleClientID = "myClientID"
private let userDefaultsLoginMethodKey = "LoginMethod"
private let userDefaultsAppTokenKey = "AppToken"
var googleSignIn: GPPSignIn
// google auto login is async, need to save handlers
var autoLoginCompletionHandler : (() -> Void)?
var autoLoginErrorHandler : ((err: String) -> Void)?
class var sharedInstance : LoginHelper {
struct Singleton {
static let instance : LoginHelper = LoginHelper()
}
return Singleton.instance
}
enum LoginMethod : String {
case NotLoggedIn = "NotLoggedIn"
case Facebook = "facebook"
case Google = "google"
case MyApp = "MyApp"
func token() -> String? {
switch (self) {
case .Google:
if LoginHelper.sharedInstance.googleSignIn.authentication != nil {
return LoginHelper.sharedInstance.googleSignIn.authentication.accessToken
}
case .Facebook:
if FBSession.activeSession().isOpen {
if let tokenData = FBSession.activeSession().accessTokenData {
return tokenData.accessToken
}
}
default:
return nil
}
return nil
}
}
var loginMethod : LoginMethod {
get {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
if let method = defaults.stringForKey(userDefaultsLoginMethodKey) {
if let loginMethod = LoginMethod(rawValue: method) {
return loginMethod
}
}
return LoginMethod.NotLoggedIn
}
set {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.setValue(newValue.rawValue, forKey: userDefaultsLoginMethodKey)
}
}
var accessToken : String? {
get {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
if let token = defaults.stringForKey(userDefaultsAppTokenKey) {
return token
}
return nil
}
set {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.setValue(newValue, forKey: userDefaultsAppTokenKey)
}
}
private override init() {
googleSignIn = GPPSignIn.sharedInstance()
googleSignIn.clientID = googleClientID
googleSignIn.shouldFetchGooglePlusUser = true
googleSignIn.shouldFetchGoogleUserEmail = true
googleSignIn.scopes = [kGTLAuthScopePlusLogin, kGTLAuthScopePlusUserinfoEmail]
}
func loginToServer(method : LoginMethod, completionHandler : (() -> Void)? = nil, errorHandler : ((err : String) -> Void)? = nil) {
if let token = loginMethod.token() {
// login to server with token
}
}
func autoLogin(completionHandler : () -> Void, errorHandler : (err : String) -> Void) {
switch (loginMethod) {
case .Google:
googleSignIn.delegate = self
if googleSignIn.trySilentAuthentication() {
// actual login will occur when finishedWithAuth is triggered,
// keep completionHandler & errorHandler to be used later
autoLoginCompletionHandler = completionHandler
autoLoginErrorHandler = errorHandler
} else {
errorHandler(err: "silent authentication failed for Google")
}
case .Facebook:
if FBSession.openActiveSessionWithAllowLoginUI(false) {
loginToServer(loginMethod, completionHandler: completionHandler, errorHandler: errorHandler)
} else {
errorHandler(err: "failed to login automaticlly to Facebook")
}
default:
Logger.debug("Skip server login for \(loginMethod.rawValue)")
completionHandler()
}
}
func finishedWithAuth(auth: GTMOAuth2Authentication, error: NSError! ) -> Void {
if let err = error {
Logger.error("Failed to login with Google")
autoLoginErrorHandler?(err: "failed to login automaticlly to Google")
} else {
// login to server
loginToServer(LoginMethod.Google, completionHandler: autoLoginCompletionHandler, errorHandler: autoLoginErrorHandler)
}
}
func didDisconnectWithError(error: NSError!) -> Void{
}
}
请注意,LoginHelper 必须扩展NSObject,如this post中所述。