苹果登录集成 Sign in with Apple

原创: 前行哲 iOS知识分享 今天

通过本文,你将了解到是否需要集成 Sign in with Apple 功能,以及如何集成 Sign in with Apple 功能。

本文主要讲解以下内容

∙ 概览
∙ 集成

概览

在 WWDC 2019 上,苹果推出了自家的 Sign in with Apple 功能,这很 Apple。可能苹果看到第三方登录百家争鸣,琢磨着自己也搞了个,这对很多第三方登录来说可能是个威胁。
现在如果app中集成了第三方登录必须支持apple登录,不然就会被拒4.8,所以集成apple登录势在必行。
苹果对 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.
使用 Sign in with Apple 会更加方便、快捷、安全,苹果不会追踪用户在应用中的行为。所以,对于用户来说使用 Sign in with Apple 会更加安全。

另外,􏰎􏰏􏰐Sign in with Apple 支持跨平台

∙ Native SDK 支持􏲟􏲠 iOS/MacOS/watchOS/tvOS
∙ Javascript SDK 支持 Android, Windows, Web
话说这个 iOS 13 才支持的功能,我们有必要集成吗?🤔

看了下面这句话,你或许就有答案了。

Sign In with Apple will be available for beta testing this summer. It will be required as an option for users in apps that support third-party sign-in when it is commercially available later this year.
简单来说,如果你的 App 没有提供第三方登录,那就不用集成。如果用到了第三方登录,那么需要提供 Sign in with Apple。

集成

集成 需要以下几个步骤:


image

一、准备工作

开启 Sign in with Apple 功能

  1. 登录开发者网站,在需要添加 Sign in with Apple 功能的 Identifier 开启功能。
image
  1. Xcode 里面 Signing & Capabilities 开启 Sign in with Apple 功能。
image

二、代码集成

Talk is cheap. Show me the code!😌

  1. 创建登录按钮
    官方提供了一个 ASAuthorizationAppleIDButton (继承自UIControl),使用这个来创建一个登录按钮。
[button addTarget:self
           action:@selector(signInWithApple)
 forControlEvents:UIControlEventTouchUpInside];
button.center = self.view.center;
[self.view addSubview:button];

这个按钮具有两种文案类型和三个样式,分别是:

typedef NS_ENUM(NSInteger, ASAuthorizationAppleIDButtonType) {
    ASAuthorizationAppleIDButtonTypeSignIn,
    ASAuthorizationAppleIDButtonTypeContinue,

    ASAuthorizationAppleIDButtonTypeDefault = ASAuthorizationAppleIDButtonTypeSignIn,
}
typedef NS_ENUM(NSInteger, ASAuthorizationAppleIDButtonStyle) {
    ASAuthorizationAppleIDButtonStyleWhite,
    ASAuthorizationAppleIDButtonStyleWhiteOutline,
    ASAuthorizationAppleIDButtonStyleBlack,
}

通过图片可能更直观

image

从图上可以看出:

∙ Apple 提供的登录按钮有三种外观:白色,带有黑色轮廓线的白色和黑色。
∙ 文案有两种:Sign In with Apple 和 Continue with Apple。(具体使用哪个文案,根据自身业务需求来定)
另外,按钮宽高默认值为 {width:130, height:30}。

对于 ASAuthorizationAppleIDButton 我们能够自定义的东西比较少,比如背景色不能更改,文案只有两种可选,并且值不能修改,可以调整的只有圆角cornerRadius和size 。

image

宽高也有一定限制:

Minimum width Minimum height Minimum margin
140pt (140px @1x, 280px @2x) 30pt (30px @1x, 60px @2x) 1/10 of the button's height

具体的设计规范,请参考:Human Interface Guidelines

通过调整 size 发现还有个新花样。比如这样:

image

是不是发现文字没有了,当然有些童鞋可能会说,变成圆形的了。嗯,圆形还是比较简单设置的,主要的变化还是文字。同时,我们还可以发现,在这种情况下,调大尺寸,里面的苹果 logo 也会跟着变大,适应展示。具体怎么操作,看下面:

ASAuthorizationAppleIDButton *button = [ASAuthorizationAppleIDButton buttonWithType:ASAuthorizationAppleIDButtonTypeContinue style:ASAuthorizationAppleIDButtonStyleBlack];
button.bounds = CGRectMake(0, 0, 40, 40);
button.cornerRadius = 20.0f;

不过上面这种方式,不知道审核是否可以通过哈,大家还是慎用,仅作为一种参考。不过,苹果貌似没有强制使用默认的登录按钮。

To help people set up an account and sign in, it's best to use the familiar buttons that Apple provides for Sign In with Apple.
再补充一点,通过上面这种修改 size 后,会收到布局警告⚠️,通过警告信息,大致可以看出来,按钮宽度的范围是:>= 130px,高度范围是:>=30px && <=64px 。

本地化:必要且重要的一点

不知道大家有没有发现,按钮显示的文字都是英文的,而且我们不可以修改文字,那我总不能在国内也展示个英文吧🙄。其实,这是个基础性的问题,知道的童鞋可以跳过了,不清楚的童鞋继续看。解决这个问题,很简单,要知道 Apple 已经为我们做了很多的工作,简单来说,我们只需要添加本地化支持就可以了,此方法适用于一切系统文案,比如我们创建的 UIBarButtonSystemItemSave ,添加简体中文支持后,他显示的文案就变成 保存 啦。

image

上图就是添加本地化语言,还不会的童鞋,自己网上查查吧,这里不在展开了。

下面是添加了本地语言之后的效果:


image
  1. Authorization 发起授权登录请求
- (void)signInWithApple API_AVAILABLE(ios(13.0))
{
    ASAuthorizationAppleIDProvider *provider = [[ASAuthorizationAppleIDProvider alloc] init];
    ASAuthorizationAppleIDRequest *request = [provider createRequest];
    request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
    
    ASAuthorizationController *vc = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
    vc.delegate = self;
    vc.presentationContextProvider = self;
    [vc performRequests];
}

解析:

∙ASAuthorizationAppleIDProvider 这个类比较简单,头文件中可以看出,主要用于创建一个 ASAuthorizationAppleIDRequest 以及获取对应 userID 的用户授权状态。在上面的方法中我们主要是用于创建一个 ASAuthorizationAppleIDRequest ,用户授权状态的获取后面会提到。

∙ 给创建的 request 设置 requestedScopes ,这是个 ASAuthorizationScope 数组,目前只有两个值,ASAuthorizationScopeFullName 和 ASAuthorizationScopeEmail,根据需求去设置即可。
∙ 然后,创建 ASAuthorizationController ,它是管理授权请求的控制器,给其设置 delegate 和 presentationContextProvider ,最后启动授权 performRequests 。

//设置上下文

ASAuthorizationControllerPresentationContextProviding 就一个方法,主要是告诉 ASAuthorizationController 展示在哪个 window 上。

#pragma mark - ASAuthorizationControllerPresentationContextProviding

- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0))
{
    return self.view.window;
}
  1. Verification 授权
    用户发起授权请求后,系统就会弹出用户登录验证的页面。
image

在用户没有同意授权之前或者取消授权之后,点击登录的时候,都会弹出上面这个界面,在这个授权页面,我们可以修改自己的用户名,以及可以选择共享我的电子邮箱或者隐藏邮件地址。这样一来,就可以达到隐藏自己真实信息的目的。

授权一次后,再次点击登录按钮,则会直接弹出下面这个窗口:


image

授权回调处理

下面是 ASAuthorizationControllerDelegate 方法,一个是授权成功的回调,一个是失败的回调。

#pragma mark - ASAuthorizationControllerDelegate

- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0))
{
    if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]])       {
        ASAuthorizationAppleIDCredential *credential = authorization.credential;
        
        NSString *state = credential.state;
        NSString *userID = credential.user;
        NSPersonNameComponents *fullName = credential.fullName;
        NSString *email = credential.email;
        NSString *authorizationCode = [[NSString alloc] initWithData:credential.authorizationCode encoding:NSUTF8StringEncoding]; // refresh token
        NSString *identityToken = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding]; // access token
        ASUserDetectionStatus realUserStatus = credential.realUserStatus;
        
        NSLog(@"state: %@", state);
        NSLog(@"userID: %@", userID);
        NSLog(@"fullName: %@", fullName);
        NSLog(@"email: %@", email);
        NSLog(@"authorizationCode: %@", authorizationCode);
        NSLog(@"identityToken: %@", identityToken);
        NSLog(@"realUserStatus: %@", @(realUserStatus));
    }
}

- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0))
{
    NSString *errorMsg = nil;
    switch (error.code) {
        case ASAuthorizationErrorCanceled:
            errorMsg = @"用户取消了授权请求";
            break;
        case ASAuthorizationErrorFailed:
            errorMsg = @"授权请求失败";
            break;
        case ASAuthorizationErrorInvalidResponse:
            errorMsg = @"授权请求响应无效";
            break;
        case ASAuthorizationErrorNotHandled:
            errorMsg = @"未能处理授权请求";
            break;
        case ASAuthorizationErrorUnknown:
            errorMsg = @"授权请求失败未知原因";
            break;
    }
    NSLog(@"%@", errorMsg);
}

当我们授权成功后,我们可以在 authorizationController:didCompleteWithAuthorization: 这个代理方法中获取到 ASAuthorizationAppleIDCredential ,通过这个可以拿到用户的 userID、email、fullName、authorizationCode、identityToken 以及 realUserStatus 等信息。

这些信息具体含义和用途:

∙ User ID: Unique, stable, team-scoped user ID,苹果用户唯一标识符,该值在同一个开发者账号下的所有 App 下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来。

∙ Verification data: Identity token, code,验证数据,用于传给开发者后台服务器,然后开发者服务器再向苹果的身份验证服务端验证本次授权登录请求数据的有效性和真实性,详见 Sign In with Apple REST API。如果验证成功,可以根据 userIdentifier 判断账号是否已存在,若存在,则返回自己账号系统的登录态,若不存在,则创建一个新的账号,并返回对应的登录态给 App。

∙ Account information: Name, verified email,苹果用户信息,包括全名、邮箱等。

∙ Real user indicator: High confidence indicator that likely real user,用于判断当前登录的苹果账号是否是一个真实用户,取值有:unsupported、unknown、likelyReal。

失败情况会走 authorizationController:didCompleteWithError: 这个方法,具体看代码吧。

  1. Handling Changes
    通过上面的步骤一个完整的授权,已经完成。BUT,我们还需要处理一些 Case。

∙ 用户终止 App 中使用 Sign in with Apple 功能
∙ 用户在设置里注销了 AppleId

这些情况下,App 需要获取到这些状态,然后做退出登录操作,或者重新登录。
我们需要在 App 启动的时候,通过 getCredentialState:completion: 来获取当前用户的授权状态。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    if (@available(iOS 13.0, *)) {
        NSString *userIdentifier = 钥匙串中取出的 userIdentifier;
        if (userIdentifier) {
            ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
            [appleIDProvider getCredentialStateForUserID:userIdentifier
                                              completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState,
                                                           NSError * _Nullable error)
            {
                switch (credentialState) {
                    case ASAuthorizationAppleIDProviderCredentialAuthorized:
                        // The Apple ID credential is valid
                        break;
                    case ASAuthorizationAppleIDProviderCredentialRevoked:
                        // Apple ID Credential revoked, handle unlink
                        break;
                    case ASAuthorizationAppleIDProviderCredentialNotFound:
                        // Credential not found, show login UI
                        break;
                }
            }];
        }
    }
    
    return YES;
}

ASAuthorizationAppleIDProviderCredentialState 解析如下:
∙ASAuthorizationAppleIDProviderCredentialAuthorized 授权状态有效;∙ASAuthorizationAppleIDProviderCredentialRevoked 上次使用苹果账号登录的凭据已被移除,需解除绑定并重新引导用户使用苹果登录;
∙ASAuthorizationAppleIDProviderCredentialNotFound 未登录授权,直接弹出登录页面,引导用户登录。

另外,在 App 使用过程中,你还可以通过通知方法来监听 revoked 状态,可以添加 ASAuthorizationAppleIDProviderCredentialRevokedNotification 这个通知,收到这个通知的时候,我们可以:

∙ Sign user out on this device
∙ Guide to sign in again
具体怎么添加和处理,可以根据业务需求来决定。

- (void)observeAppleSignInState
{
    if (@available(iOS 13.0, *)) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleSignInWithAppleStateChanged:)
                                                     name:ASAuthorizationAppleIDProviderCredentialRevokedNotification
                                                   object:nil];
    }
}

- (void)handleSignInWithAppleStateChanged:(NSNotification *)notification
{
    // Sign the user out, optionally guide them to sign in again
    NSLog(@"%@", notification.userInfo);
}

One more thing
除此之外,苹果还把 iCloud KeyChain password 集成到了这套 API 里,我们在使用的时候,只需要在创建 request 的时候,多创建一个 ASAuthorizationPasswordRequest,这样如果 KeyChain 里面也有登录信息的话,可以直接使用里面保存的用户名和密码进行登录。代码如下:

- (void)perfomExistingAccountSetupFlows API_AVAILABLE(ios(13.0))
{
    ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
    ASAuthorizationAppleIDRequest *authAppleIDRequest = [appleIDProvider createRequest];
    ASAuthorizationPasswordRequest *passwordRequest = [[ASAuthorizationPasswordProvider new] createRequest];
    
    NSMutableArray <ASAuthorizationRequest *>* array = [NSMutableArray arrayWithCapacity:2];
    if (authAppleIDRequest) {
        [array addObject:authAppleIDRequest];
    }
    if (passwordRequest) {
        [array addObject:passwordRequest];
    }
    NSArray <ASAuthorizationRequest *>* requests = [array copy];
    
    ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:requests];
    authorizationController.delegate = self;
    authorizationController.presentationContextProvider = self;
    [authorizationController performRequests];
}

#pragma mark - ASAuthorizationControllerDelegate

- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0))
{
    if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) {
        ASPasswordCredential *passwordCredential = authorization.credential;
        NSString *userIdentifier = passwordCredential.user;
        NSString *password = passwordCredential.password;
        
        NSLog(@"userIdentifier: %@", userIdentifier);
        NSLog(@"password: %@", password);
    }
}

以上就是关于 Sign in with Apple 的相关内容和集成方法。以上内容特别鸣谢 前行哲同学,分享出来供大家一起学习。如有侵权,请联系删除

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容