“ 我是如此的相信,在背后支撑的是你,一直与我并肩而行,仰望等太阳升起,听见鸟群回来的声音 ”
——我是如此的相信
为什么写这篇文章呢?主要出于以下两点考虑:
首次上架的APP,如果不接入SIWA,抱歉,拒绝上架;
google到的文章质量不高、高度重复,甚至没有一篇可以直接拿来复现的;
于是借助项目的机会来实战,并把完整的过程分享给需要的工程师们。
01
—
SIWA简介

时序图
02
—
开始前准备
环境准备:Xcode 11 、 Mac OS X 10.15 + 、iOS 13.0 +
Xcode具体用什么版本需要结合iOS版本来使用。比如Xcode11 Beta版本就不支持iOS13.3.导致编译成功后无法在设备上运行。优先下载正式版的Xcode即可
在开发者后台打开SIWA权限并重新生成profile文件导入Xcode Xcode中打开SIWA 前端代码接入 & 后端验证代码接入
03
—
实战
SIWA实施分为三个步骤:配置权限、前端代码接入、后端验证。
1、配置权限
权限配置涉及开发者后台和Xcode工具。
开发者后台:进入Certificates, Identifiers & Profiles -> 选择 Identifiers -> 勾选SIWA选项

开发者后台配置示意图
Xcode工具:Xcode选择对应的Target -> select 'signing & capabilities' -> 双击SIWA添加能力

Xcode配置示意图
2、前端代码接入
代码集成流程如下:

页面嵌入按钮
按钮是苹果提供的,允许做一些样式调整。该按钮要求在登录页面的显眼位置。
if (@available(iOS 13.0, *)) {// Sign In With Apple ButtonASAuthorizationAppleIDButton *appleLoginBtn = [ASAuthorizationAppleIDButton buttonWithType:ASAuthorizationAppleIDButtonTypeDefault style:ASAuthorizationAppleIDButtonStyleWhite];appleLoginBtn.frame = CGRectMake(30, 30, 200, 44);appleLoginBtn.cornerRadius = 22.f;[appleLoginBtn addTarget:self action:@selector(appleLoginBtnDidClicked:) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:appleLoginBtn];}
点击按钮后触发登录验证请求
- (void)appleLoginBtnDidClicked:(id)sender{if (@available(iOS 13.0, *)) {ASAuthorizationAppleIDRequest *appleIDRequest = [[ASAuthorizationAppleIDProvider new] createRequest];appleIDRequest.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[appleIDRequest]];// 设置代理authorizationController.delegate = self;authorizationController.presentationContextProvider = self;// 发起请求[authorizationController performRequests];}}
发送请求的同时需要实现一些代理方法:
apple登录弹窗的载体设置
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)){return self.view.window; // or [UIApplication sharedApplication].windows.lastObject;}请求成功回调
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)){if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {// 用户使用AppleId登录ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;NSString *user = appleIDCredential.user;// 用户数据NSString *familyName = appleIDCredential.fullName.familyName;NSString *givenName = appleIDCredential.fullName.givenName;NSString *email = appleIDCredential.email;// 服务器验证所需的参数NSData *identityToken = appleIDCredential.identityToken;NSData *authorizationCode = appleIDCredential.authorizationCode;// 这里调自己的后端服务,由后端跟apple后台服务通信,并返回项目自身的用户数据(如果账号不存在,则新建一个返回)}}请求失败回调
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)){switch (error.code) {case ASAuthorizationErrorCanceled:errorMsg = @"用户取消了授权请求";break;case ASAuthorizationErrorFailed:errorMsg = @"授权请求失败";break;case ASAuthorizationErrorInvalidResponse:errorMsg = @"授权请求响应无效";break;case ASAuthorizationErrorNotHandled:errorMsg = @"未能处理授权请求";break;case ASAuthorizationErrorUnknown:errorMsg = @"授权请求失败未知原因";break;default:break;}// do what you want to do here}
以上就是基础版前端工作,如果想支持钥匙串记录密码并使用密码登录的话,还需要做一些额外操作。
支持钥匙串需要做三点:导入Security.framework(无则加勉)、请求时追加钥匙串选项、请求回调中追加钥匙串处理。
请求时追加钥匙串选项:
- (void)appleLoginBtnDidClicked:(id)sender{if (@available(iOS 13.0, *)) {ASAuthorizationAppleIDRequest *request = [[ASAuthorizationAppleIDProvider new] createRequest];[request setRequestedScopes:@[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail]];ASAuthorizationController *appleSignController;// if 新用户登陆 (keychain 中未记录用户账户信息)appleSignController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];// else 旧用户登陆,并且 keychain 中保存了用户的账户信息,可以同时使用 appleId登录 和 钥匙串登录ASAuthorizationPasswordRequest *passwordRequest = [[ASAuthorizationPasswordProvider new] createRequest];appleSignController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request, passwordRequest]];// end if// 设置代理appleSignController.delegate = self;appleSignController.presentationContextProvider = self;// 发起请求[appleSignController performRequests];}}
请求回调中追加钥匙串处理:
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)){if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {// 用户使用appleId登录// do everything like before// ...// 把账号信息存入钥匙串} else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]){// 用户使用钥匙串登录ASPasswordCredential *passwordCredential = authorization.credential;NSString *user = passwordCredential.user;NSString *password = passwordCredential.password;// 不再请求apple服务验证,直接使用账号信息登录。}}
3、后端验证
前端在请求成功回调中,将拿到的identityToken和authorizationCode传给后端,
后端携带这些数据与apple服务器通信请求验证和公钥。
验证成功后返回项目内部的账号给前端登录。具体的事情后端老哥可以的,相信他!
04
—
最后
考虑到用户可能会在使用SIWA登录后,可能在设置中将其解绑或者切换其他AppleId账号,那么APP需要引起用户重新登录。
设置路径如下:设置 -> AppleId -> 密码与安全性 -> 使用您AppleId的App -> 选择App -> 停止使用AppleID

使用AppleID的App

解绑
用户解绑时,APP可能正在后台运行,也可能被kill了。因此,需要在应用启动和进入前台时检测凭证状态credentialState,并依据实际状态作对应处理。
应用启动检查:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {if (@available(iOS 13.0, *)) {NSString *userIdentifier = 钥匙串中取出的 userIdentifier;if (userIdentifier) {ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];[appleIDProvider getCredentialStateForUserID:userIdentifiercompletion:^(ASAuthorizationAppleIDProviderCredentialState credentialState,NSError * _Nullable error){switch (credentialState) {case ASAuthorizationAppleIDProviderCredentialAuthorized:// do nothingbreak;case ASAuthorizationAppleIDProviderCredentialRevoked:// AppleId遭解绑,引导用户重新登录break;case ASAuthorizationAppleIDProviderCredentialNotFound:// 引导用户重新登录break;}}];}}return YES;}
应用进入前台检查:
if (@available(iOS 13.0, *)) {[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(handleSignInWithAppleStateChanged:)name:ASAuthorizationAppleIDProviderCredentialRevokedNotificationobject:nil];}- (void)handleSignInWithAppleStateChanged:(NSNotification *)notification{// 引导用户重新登录}
05
—
参考文献
https://arctouch.com/blog/how-to-integrate-sign-in-with-apple/




