Sign In with Apple 登录

3,911 阅读5分钟

Sign In with Apple 介绍

1 Sign in with Apple (苹果登录),苹果登录是 iOS13 新增加的功能,当你的应用使用了第三方登录比如微信登录,同时也需要集成苹果登录,否则提交审核可能会被拒绝
2 添加处都要添加iOS 13 的判断,可以自定义,也可以使用苹果自带的按钮展示样式,查看了几个大的APP,基本都是用的苹果自带的登录样式。(优先建议苹果自带样式)
3 对于 ASAuthorizationAppleIDButton 我们能够自定义的东西比较少,比如不能修改背景色,文案只有三种可以选择,可以调整的只有圆角 cornerRadius 和尺寸 size 。

苹果展示的授权界面

授权界面的含义

登录按钮和自定义按钮

1 系统自带的按钮展示效果 3种文案

苹果自带的登录效果

  • 如果是准备自定义按钮的,请参考苹果自定义按钮的规则,免得审核被拒。
  • 按钮必须在显著的位置(避免滑动屏幕才能看到)
  • 登录按钮有三种外观:白色,带有黑色轮廓线的白色和黑色,其他设计可能会影响审核;
  • 按钮圆角范围及按钮最小尺寸也有要求(官方文档);
2 xcode首先添加配置 (这里需要有一个注意的地方,如果过个targets,如果某一个targets有用的是企业证书,企业证书是没有苹果登录功能的,所有需要单独配置entitlements)

3 代码实现

// 导入头文件
#import <AuthenticationServices/AuthenticationServices.h>

ASAuthorizationAppleIDButton *loginBtn = [[ASAuthorizationAppleIDButton alloc]initWithAuthorizationButtonType:ASAuthorizationAppleIDButtonTypeSignIn authorizationButtonStyle:ASAuthorizationAppleIDButtonStyleWhiteOutline];
[loginBtn addTarget:self action:@selector(signInWithApple) forControlEvents:UIControlEventTouchUpInside];
loginBtn.frame = CGRectMake((SCREEN_WIDTH-200)/2, 0, 200, 40);
loginBtn.cornerRadius = 40/2;
[self addSubview:loginBtn];
- (void)signInWithApple{
    if (@available(iOS 13.0, *)) {
    ASAuthorizationAppleIDProvider * appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
    ASAuthorizationAppleIDRequest * authAppleIDRequest = [appleIDProvider createRequest];
    ASAuthorizationPasswordRequest * passwordRequest = [[[ASAuthorizationPasswordProvider alloc] init] 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];
    } else {
    // 处理不支持系统版本
    NSLog(@"系统不支持Apple登录");
    }
}

后面设置爱2个代理

apple登录对于所有设备,比如iPhone、iPad、Mac,只要是同一个开发者账号的,同一个用户在所有软件中返回的openid都是相同的,类似于QQ登录和微信登录中的unionid

#pragma mark- ASAuthorizationControllerDelegate
// 授权成功
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization {
        if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
        //高能预警
        //授权信息里的用户信息(email、NSPersonNameComponents对象所有属性)当且仅当第一次授权时才会返回,之后就算 停止使用 Apple ID 再重新授权都不会返回用户信息
        ASAuthorizationAppleIDCredential * credential = authorization.credential;
        
        // 苹果用户唯一标识符,该值在同一个开发者账号下的所有 App 下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来。
        NSString * userID = credential.user;
        
        // 苹果用户信息 如果授权过,可能无法再次获取该信息
        NSPersonNameComponents * fullName = credential.fullName;
        NSString * email = credential.email;
        
        // 服务器验证需要使用的参数
        NSString * authorizationCode = [[NSString alloc] initWithData:credential.authorizationCode encoding:NSUTF8StringEncoding];
        NSString * identityToken = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding];
        
        // 用于判断当前登录的苹果账号是否是一个真实用户,取值有:unsupported、unknown、likelyReal
        ASUserDetectionStatus realUserStatus = credential.realUserStatus;
        if (userID.length > 0) {
            [GCSaveDataTool saveUserData:userID forKey:GCAppleIDNSUserDefaults];
        }
//        given name等同于first name是名,教名。如字面意思,父母或者教父给起的名字。
//
//        middle name是名和姓之间的名字,是父母或亲戚所取,一般取长者的名或姓。
//
//        familiy name是姓氏,家族名FAMILY是家庭的意思,姓是家庭共有的,可以这样理解。last name
//
//        解析
//
//        英语姓名的一般结构为:自取名+教名+姓。given name +  familiy name
        NSString *nickName;
        if (!ZHStringIsEmpty(fullName.givenName) || !ZHStringIsEmpty(fullName.familyName)) {
            nickName = [NSString stringWithFormat:@"%@%@",fullName.givenName,fullName.familyName];
        }
         
        if (!ZHStringIsEmpty(nickName)) {
            [GCSaveDataTool saveUserData:nickName forKey:GCAppleFullNameNSUserDefaults];
        }
        
        
        NSLog(@"userID: %@  familyName %@", userID,fullName.familyName);
        
     
        NSLog(@"nickName: %@", nickName);
        NSLog(@"email: %@", email);
        NSLog(@"authorizationCode: %@", authorizationCode);
        NSLog(@"identityToken: %@", identityToken);
        NSLog(@"realUserStatus: %@", @(realUserStatus));
        
        if (self.authorizationBlock) {
            self.authorizationBlock(YES, userID);
        }
        
    }
}
// 授权失败
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error {
        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;
    }
}

#pragma mark- ASAuthorizationControllerPresentationContextProviding
//ASAuthorizationControllerPresentationContextProviding 主要是告诉 ASAuthorizationController 在哪个 window 上显示。
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller {
    return self.view.window;
}

具体注意事项

1用户注销 AppleId 或 停止使用 Apple ID 的状态处理 这个里面需要监测appid 的状态,处理一些业务逻辑。但是我发现这里没有什么可处理的业务逻辑,所以我只没有写,如果大家有写的,可以一起讨论下
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    if (@available(iOS 13.0, *)) {
        // 注意 存储用户标识信息需要使用钥匙串来存储 这里使用NSUserDefaults 做的简单示例
        NSString * userIdentifier = [[NSUserDefaults standardUserDefaults] valueForKey:@"appleID"];
        if (userIdentifier) {
            ASAuthorizationAppleIDProvider * appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
            [appleIDProvider getCredentialStateForUserID:userIdentifier
                                              completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError * _Nullable error) {
                switch (credentialState) {
                    case ASAuthorizationAppleIDProviderCredentialAuthorized:
                        // 授权状态有效
                        break;
                    case ASAuthorizationAppleIDProviderCredentialRevoked:
                        // 苹果账号登录的凭据已被移除,需解除绑定并重新引导用户使用苹果登录
                        break;
                    case ASAuthorizationAppleIDProviderCredentialNotFound:
                        // 未登录授权,直接弹出登录页面,引导用户登录
                        break;
                    case ASAuthorizationAppleIDProviderCredentialTransferred:
                        // 授权AppleID提供者凭据转移
                        break;
                }
            }];
        }
    }
    return YES;
}
2 还有一个通知的监测(这个我也没有做处理,不知道有做处理的吗)
// 注册通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleSignInWithAppleStateChanged:) name:ASAuthorizationAppleIDProviderCredentialRevokedNotification object:nil];
#pragma mark- apple授权状态 更改通知
- (void)handleSignInWithAppleStateChanged:(NSNotification *)notification
{
    NSLog(@"%@", notification.userInfo);
}

一起探讨的

这个难度到时不高,就是想和大家一起探讨的是,这个通知和- (BOOL)application里面的监测状态变化,大家有没有做处理。

参考文档