KMM+Compose Multiplatform实现Apple SignIn卡在密码输入页求助
KMM + Compose Multiplatform 实现Apple Sign In时输入密码后流程卡住的问题排查与解决
问题核心
在KMM与Compose Multiplatform项目中实现Apple Sign In,输入密码后流程卡住,无任何日志或回调反馈,已完成开发者后台配置、依赖集成与登录逻辑编写。
可能原因与解决步骤
1. iOS窗口获取方式错误(iOS 13+多场景架构适配问题)
你的AuthorizationDelegate中使用UIApplication.sharedApplication.keyWindow!!获取窗口,但iOS 13之后引入了多场景(Scene)架构,keyWindow已被废弃,直接使用可能导致授权界面无法正确关闭,流程卡住。
修改方案:
替换presentationAnchorForAuthorizationController方法,从活跃场景中获取当前窗口:
override fun presentationAnchorForAuthorizationController(controller: ASAuthorizationController): UIWindow { val activeScene = UIApplication.sharedApplication.connectedScenes .firstOrNull { it.activationState == UISceneActivationStateForegroundActive } as? UIWindowScene return activeScene?.windows?.firstOrNull { it.isKeyWindow } ?: UIWindow() }
2. Delegate被意外回收(生命周期问题)
你在signIn方法中创建的AuthorizationDelegate是局部变量,Kotlin/Native的内存管理可能在方法执行完毕后回收该对象,导致授权完成后无法触发回调。
修改方案:
将AuthorizationDelegate作为AppleSignInManager的成员变量持有,避免被回收:
actual class AppleSignInManager actual constructor() { private var authDelegate: AuthorizationDelegate? = null actual fun signIn(listener: AppleSignInListener) { val logger = Logger.withTag("AppleSignInManager") val provider = ASAuthorizationAppleIDProvider() val request = provider.createRequest() request.requestedScopes = listOf(ASAuthorizationScopeFullName, ASAuthorizationScopeEmail) logger.i { "requestedScopes : ${ASAuthorizationScopeFullName.toString()} : ${ASAuthorizationScopeEmail.toString()} " } val controller = ASAuthorizationController(authorizationRequests = listOf(request)) authDelegate = AuthorizationDelegate(listener, logger) controller.delegate = authDelegate controller.presentationContextProvider = authDelegate controller.performRequests() } }
3. 日志输出失效
自定义Logger可能存在配置问题,导致即使回调触发也看不到日志。可以先使用iOS原生日志工具验证回调是否执行:
在AuthorizationDelegate的回调方法中添加:
import platform.Foundation.NSLog // 在didCompleteWithAuthorization方法中 NSLog("Apple Sign In 授权完成回调触发") // 在didCompleteWithError方法中 NSLog("Apple Sign In 错误回调触发: ${didCompleteWithError.localizedDescription}")
4. 环境与配置验证
- 必须使用真机测试:Apple Sign In在模拟器上可能存在兼容性问题
- 确认设备网络正常:Apple授权流程需要网络连接
- 检查开发者后台配置:确保App ID已启用Sign In with Apple,Bundle ID与项目一致,Provisioning Profile是最新生成并包含该权限
修改后的完整示例(关键部分)
actual class AppleSignInManager actual constructor() { private var authDelegate: AuthorizationDelegate? = null actual fun signIn(listener: AppleSignInListener) { val logger = Logger.withTag("AppleSignInManager") val provider = ASAuthorizationAppleIDProvider() val request = provider.createRequest() request.requestedScopes = listOf(ASAuthorizationScopeFullName, ASAuthorizationScopeEmail) logger.i { "requestedScopes : ${ASAuthorizationScopeFullName.toString()} : ${ASAuthorizationScopeEmail.toString()} " } val controller = ASAuthorizationController(authorizationRequests = listOf(request)) authDelegate = AuthorizationDelegate(listener, logger) controller.delegate = authDelegate controller.presentationContextProvider = authDelegate controller.performRequests() } } class AuthorizationDelegate(private val listener: AppleSignInListener, val logger: Logger) : NSObject(), ASAuthorizationControllerDelegateProtocol, ASAuthorizationControllerPresentationContextProvidingProtocol { override fun authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization: ASAuthorization) { NSLog("Apple Sign In 授权完成回调触发") logger.i {"didCompleteWithAuthorization"} val credential = (didCompleteWithAuthorization.credential as? ASAuthorizationAppleIDCredential) val idTokenData = credential?.identityToken val idToken = idTokenData?.toUtf8String() if (idToken != null) { logger.i {didCompleteWithAuthorization.toString()} listener.onSignInSuccess(idToken) } else { logger.i {didCompleteWithAuthorization.toString()} listener.onSignInFailure(Throwable()) } } override fun authorizationController(controller: ASAuthorizationController, didCompleteWithError: NSError) { NSLog("Apple Sign In 错误回调触发: ${didCompleteWithError.localizedDescription}") logger.i {didCompleteWithError.localizedDescription} listener.onSignInFailure(Throwable()) } override fun presentationAnchorForAuthorizationController(controller: ASAuthorizationController): UIWindow { val activeScene = UIApplication.sharedApplication.connectedScenes .firstOrNull { it.activationState == UISceneActivationStateForegroundActive } as? UIWindowScene return activeScene?.windows?.firstOrNull { it.isKeyWindow } ?: UIWindow() } } fun NSData.toUtf8String(): String? { return NSString.create(data = this, encoding = NSUTF8StringEncoding) as String? }
内容的提问来源于stack exchange,提问作者Dan Luciano
相关产品推荐
相关产品推荐

