swift学习记录

Sign In with Apple

2019-10-08  本文已影响0人  TitanCoder
image

Sign in with Apple

Sign in with Apple makes it easy for users to sign in to your apps and websites using their Apple ID. Instead of filling out forms, verifying email addresses, and choosing new passwords, they can use Sign in with Apple to set up an account and start using your app right away. All accounts are protected with two-factor authentication for superior security, and Apple will not track users’ activity in your app or website.

Make signing in easy

在代码集成之前还需要做一些准备工作

  1. 在开发者网站,在需要添加Sign in with Apple功能
image
  1. Xcode里面开启Sign in with Apple功能
Xcode

登录按钮

Apple苹果登录按钮, 需要使用ASAuthorizationAppleIDButton类创建添加, 该类是iOS 13苹果提供的创建Apple登录按钮的专属类

@available(iOS 13.0, *)
open class ASAuthorizationAppleIDButton : UIControl {
    // 初始化方法
    public convenience init(type: ASAuthorizationAppleIDButton.ButtonType, style: ASAuthorizationAppleIDButton.Style)

    // 初始化方法
    public init(authorizationButtonType type: ASAuthorizationAppleIDButton.ButtonType, authorizationButtonStyle style: ASAuthorizationAppleIDButton.Style)

    // 设置按钮的圆切角
    open var cornerRadius: CGFloat
}

开始创建Apple登录按钮

// apple登录按钮
let appleButton = ASAuthorizationAppleIDButton(type: .continue, style: .black)
appleButton.frame = CGRect(x: 100, y: showLabel.frame.maxY + 40, width: 200, height: 50)
appleButton.cornerRadius = 10
appleButton.addTarget(self, action: #selector(appleAction), for: .touchUpInside)
view.addSubview(appleButton)

ButtonType

public enum ButtonType : Int {
    // signIn登录类型
    case signIn
    // continue类型
    case `continue`

    public static var `default`: ASAuthorizationAppleIDButton.ButtonType { get }
}

不同ButtonType展示效果如下

ButtonType

Style

@available(iOS 13.0, *)
public enum Style : Int {
    
    case white

    case whiteOutline

    case black
}

不同Style展示效果和使用场景如下

Style

cornerRadius

设置按钮的圆角大小

// 默认值大概5左右, 具体值不知
appleButton.cornerRadius = 10

发起授权请求

@objc fileprivate func appleAction() {
    // 基于用户的Apple ID授权用户,生成用户授权请求的一种机制
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    // 创建新的AppleID授权请求
    let request = appleIDProvider.createRequest()
    // 所需要请求的联系信息
    request.requestedScopes = [.fullName, .email]
    // 管理授权请求的控制器
    let controller = ASAuthorizationController(authorizationRequests: [request])
    // 授权成功或者失败的代理
    controller.delegate = self
    // 显示上下文的代理, 系统可以在上下文中向用户展示授权页面
    controller.presentationContextProvider = self
    // 在控制器初始化期间启动授权流
    controller.performRequests()
}

delegate

设置授权控制器通知授权请求的成功与失败的代理

// 代理
weak open var delegate: ASAuthorizationControllerDelegate?

// 代理方法如下
@available(iOS 13.0, *)
public protocol ASAuthorizationControllerDelegate : NSObjectProtocol {
    // 授权成功的回调
    optional func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization)

    // 授权失败的回调
    optional func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error)
}

presentationContextProvider

需要向用户展示授权页面时, 需要遵循该协议

// 显示上下文的代理, 系统可以在上下文中向用户展示授权页面
weak open var presentationContextProvider: ASAuthorizationControllerPresentationContextProviding?

// 协议方法
@available(iOS 13.0, *)
public protocol ASAuthorizationControllerPresentationContextProviding : NSObjectProtocol {

    // 该方法返回一个视图锚点, 告诉代理应该在哪个window 展示内容给用户
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor
}


// 方法执行示例
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
    return self.view.window!
}

ASAuthorization

@available(iOS 13.0, *)
open class ASAuthorization : NSObject {

    
    // 创建发起成功授权的发起者
    open var provider: ASAuthorizationProvider { get }

    // 成功授权后返回的相关凭证, 包含授权后的相关信息,是一个协议
    open var credential: ASAuthorizationCredential { get }
}

ASAuthorizationCredential

是一个协议, 在处理授权成功的结果中, 需要使用遵循该协议的类, 有以下三个

ASPasswordCredential

@available(iOS 12.0, *)
open class ASPasswordCredential : NSObject, ASAuthorizationCredential {

    // 初始化方法
    public init(user: String, password: String)

    // 用户名
    open var user: String { get }

    // 用户密码
    open var password: String { get }
}

ASAuthorizationAppleIDCredential

@available(iOS 13.0, *)
open class ASAuthorizationAppleIDCredential : NSObject, ASAuthorizationCredential {

    // 和用户AppleID关联的用户ID(标识符)
    open var user: String { get }

    // 传送给ASAuthorizationRequest的字符串
    open var state: String? { get }

    // 用户授权的可访问的联系信息的种类
    open var authorizedScopes: [ASAuthorization.Scope] { get }

    // 为APP提供的授权证明的有效token
    open var authorizationCode: Data? { get }

    // JSON Web Token (JWT), 用于以安全的方式向应用程序传递关于用户身份的信息
    open var identityToken: Data? { get }

    // 用户的email
    open var email: String? { get }

    // 用户名
    open var fullName: PersonNameComponents? { get }

    // 用户是否是真实用户的状态
    open var realUserStatus: ASUserDetectionStatus { get }
}

// 用户是否是真实用户的枚举值
public enum ASUserDetectionStatus : Int {

    case unsupported

    case unknown

    case likelyReal
}

ASAuthorizationSingleSignOnCredential

@available(iOS 13.0, *)
open class ASAuthorizationSingleSignOnCredential : NSObject, ASAuthorizationCredential {

    // AuthenticationServices返回的字符串
    open var state: String? { get }

    // 用户获取授权范围的token
    open var accessToken: Data? { get }

    // JSON Web Token (JWT), 用于以安全的方式向应用程序传递关于用户身份的信息
    open var identityToken: Data? { get }

    // 用户授权的可访问的联系信息的种类
    open var authorizedScopes: [ASAuthorization.Scope] { get }

    // 完整的身份验证响应信息
    @NSCopying open var authenticatedResponse: HTTPURLResponse? { get }
}

授权成功

上面有提到, 在ASAuthorizationControllerDelegate有两个协议方法, 分别是授权成功和失败的回调, 下面就具体看看

extension SignViewController: ASAuthorizationControllerDelegate {
    // 处理成功的授权
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        print("授权成功")
        // 成功的Apple ID身份验证信息
        if let appleIDCreden = authorization.credential as? ASAuthorizationAppleIDCredential {
            let userIdentifier = appleIDCreden.user
            let fullName = appleIDCreden.fullName
            let email = appleIDCreden.email
            
            // 这里需要我们在系统中创建一个账户, 用于存储用户的唯一标识userIdentifier
            // 可以在系统的钥匙串中存储
            let webVC = WebViewController()
            webVC.user = userIdentifier
            webVC.giveName = fullName?.givenName ?? ""
            webVC.familyName = fullName?.familyName ?? ""
            webVC.email = email ?? ""
            navigationController?.pushViewController(webVC, animated: true)
        } else if let passwordCreden = authorization.credential as? ASPasswordCredential {
            // 密码凭证用户的唯一标识
            let userIdentifiler = passwordCreden.user
            // 密码凭证的密码
            let password = passwordCreden.password
            
            // 显示相关信息
            let message = "APP已经收到您选择的秘钥凭证\nUsername: \(userIdentifiler)\n Password: \(password)"
            showLabel.text = message
        } else {
            showLabel.text = "授权信息均不符"
        }
    }
    
    // 处理授权错误
    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        print("授权错误: \(error)")
        
        var showText = ""
        if let authError = error as? ASAuthorizationError {
            let code = authError.code
            switch code {
            case .canceled:
                showText = "用户取消了授权请求"
            case .failed:
                showText = "授权请求失败"
            case .invalidResponse:
                showText = "授权请求响应无效"
            case .notHandled:
                showText = "未能处理授权请求"
            case .unknown:
                showText = "授权请求失败, 未知的错误原因"
            default:
                showText = "其他未知的错误原因"
            }
        }
        showLabel.text = showText
    }
}

做好了上面配置, 就可以看到下面的登录页面

image name image image

监听授权状态

// ASAuthorizationAppleIDProvider提供了一个获取用户授权状态和授权凭据是否有效
func getCredentialState(forUserID: String, completion: (ASAuthorizationAppleIDProvider.CredentialState, Error?) -> Void)


// ASAuthorizationAppleIDProvider.CredentialState的所有枚举值
public enum CredentialState : Int {
    case revoked
    case authorized
    case notFound
    case transferred
}

示例代码如下

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    if #available(iOS 13.0, *) {
        // 钥匙串中取出的
        let userIdentifier = "userIdentifier"
        if (!userIdentifier.isEmpty) {
            // 基于用户的Apple ID授权用户,生成用户授权请求的一种机制
            let appleIDProvider = ASAuthorizationAppleIDProvider()
            // 返回完成处理程序中给定用户的凭据状态
            appleIDProvider.getCredentialState(forUserID: userIdentifier) { (state, error) in
                switch state {
                case .authorized:
                    print("授权状态有效")
                case .notFound:
                    print("授权凭证缺失(可能是使用AppleID 登录过App)")
                case .revoked:
                    print("上次使用苹果账号登录的凭据已被移除,需解除绑定并重新引导用户使用苹果登录")
                default:
                    print("未知状态")
                }
            }
        }
    }
    
    return true
}

除此之外还可以通过通知方法来监听revoked状态, 在ASAuthorizationAppleIDProvider中增加了一个属性, 用于监听revoked状态

@available(iOS 13.0, *)
public class let credentialRevokedNotification: NSNotification.Name

// 使用方法
fileprivate func observeAppleSignInState() {
    if #available(iOS 13.0, *) {
        let center = NotificationCenter.default
        center.addObserver(self, selector: #selector(handleStateChange(noti:)), name: ASAuthorizationAppleIDProvider.credentialRevokedNotification, object: nil)
    }
}

@objc fileprivate func handleStateChange(noti: Any) {
    print("授权状态发生改变")
}

参考文档


欢迎您扫一扫下面的微信公众号,订阅我的博客!

微信公众号
上一篇 下一篇

猜你喜欢

热点阅读