2025-11-13 19:07:59 +08:00
//
// KBLoginVM . m
//
# import "KBLoginVM.h"
# import "AppleSignInManager.h"
# import "KBNetworkManager.h"
# import "KBAuthManager.h"
# import "KBAPI.h"
# import "KBUser.h"
@ interface KBLoginVM ( )
@ property ( atomic , strong , readwrite , nullable ) KBUser * currentUser ;
@ end
@ implementation KBLoginVM
+ ( instancetype ) shared {
static KBLoginVM * vm ; static dispatch_once _t onceToken ; dispatch_once ( & onceToken , ^ { vm = [ KBLoginVM new ] ; } ) ;
return vm ;
}
- ( BOOL ) isLoggedIn {
return [ [ KBAuthManager shared ] isLoggedIn ] ;
}
- ( void ) signInWithAppleFromViewController : ( UIViewController * ) presenter
completion : ( KBLoginCompletion ) completion {
// 调 起 Apple 登 录
[ [ AppleSignInManager shared ] signInFromViewController : presenter completion : ^ ( ASAuthorizationAppleIDCredential * _Nullable credential , NSError * _Nullable error ) {
if ( error ) { if ( completion ) completion ( NO , error ) ; return ; }
if ( ! [ credential isKindOfClass : [ ASAuthorizationAppleIDCredential class ] ] ) {
2025-11-17 20:26:39 +08:00
if ( completion ) completion ( NO , [ NSError errorWithDomain : @ "KBLogin" code : -1 userInfo : @ { NSLocalizedDescriptionKey : KBLocalized ( @ "Invalid login credential" ) } ] ) ;
2025-11-13 19:07:59 +08:00
return ;
}
ASAuthorizationAppleIDCredential * cred = ( ASAuthorizationAppleIDCredential * ) credential ;
// identityToken / authorizationCode 均 可 按 后 端 要 求 传 递 ; 本 项 目 后 端 使 用 identityToken 映 射 为 code
NSString * identityToken = cred . identityToken ? [ [ NSString alloc ] initWithData : cred . identityToken encoding : NSUTF8StringEncoding ] : nil ;
NSString * authorizationCode = cred . authorizationCode ? [ [ NSString alloc ] initWithData : cred . authorizationCode encoding : NSUTF8StringEncoding ] : nil ;
NSMutableDictionary * params = [ NSMutableDictionary dictionary ] ;
if ( identityToken . length ) params [ @ "code" ] = identityToken ;
if ( authorizationCode . length ) params [ @ "accessCode" ] = authorizationCode ; // 仅 供 后 端 需 要 时 使 用
if ( cred . user . length ) params [ @ "userID" ] = cred . user ; // 可 选
// 向 服 务 端 发 起 校 验
[ [ KBNetworkManager shared ] POST : API_APPLE _LOGIN jsonBody : params headers : nil completion : ^ ( id _Nullable jsonOrData , NSURLResponse * _Nullable response , NSError * _Nullable error ) {
if ( error ) { if ( completion ) completion ( NO , error ) ; return ; }
// 从 返 回 JSON 中 提 取 token ( 支 持 常 见 命 名 与 层 级 data / user )
// 先 解 析 用 户 模 型 ( 使 用 MJExtension )
KBUser * user = [ KBUser userFromResponseObject : jsonOrData ] ;
self . currentUser = user ;
NSString * token = user . token ? : [ self . class tokenFromResponseObject : jsonOrData ] ;
if ( token . length = = 0 ) {
2025-11-17 20:26:39 +08:00
if ( completion ) completion ( NO , [ NSError errorWithDomain : @ "KBLogin" code : -2 userInfo : @ { NSLocalizedDescriptionKey : KBLocalized ( @ "No token returned" ) } ] ) ;
2025-11-13 19:07:59 +08:00
return ;
}
// 保 存 登 录 态 到 共 享 钥 匙 串 ; 供 App 与 扩 展 共 享
BOOL ok = [ [ KBAuthManager shared ] saveAccessToken : token
refreshToken : nil
expiryDate : nil
userIdentifier : cred . user ] ;
2025-11-17 20:26:39 +08:00
if ( completion ) completion ( ok , ok ? nil : [ NSError errorWithDomain : @ "KBLogin" code : -3 userInfo : @ { NSLocalizedDescriptionKey : KBLocalized ( @ "Failed to save login state" ) } ] ) ;
2025-11-13 19:07:59 +08:00
} ] ;
} ] ;
}
# pragma mark - Helpers
// 宽 松 解 析 : token / access_token / accessToken , 支 持 顶 层 或 data / user 层
+ ( NSString * ) tokenFromResponseObject : ( id ) obj {
if ( ! [ obj isKindOfClass : [ NSDictionary class ] ] ) return nil ;
NSDictionary * dict = ( NSDictionary * ) obj ;
NSString * ( ^ pick ) ( NSDictionary * ) = ^ NSString * ( NSDictionary * d ) {
NSArray * keys = @ [ @ "token" , @ "access_token" , @ "accessToken" ] ;
for ( NSString * k in keys ) {
id v = d [ k ] ; if ( [ v isKindOfClass : NSString . class ] && ( ( NSString * ) v ) . length > 0 ) return v ;
}
return nil ;
} ;
NSString * t = pick ( dict ) ;
if ( t . length ) return t ;
id data = dict [ @ "data" ] ; if ( [ data isKindOfClass : NSDictionary . class ] ) { t = pick ( data ) ; if ( t . length ) return t ; }
id user = dict [ @ "user" ] ; if ( [ user isKindOfClass : NSDictionary . class ] ) { t = pick ( user ) ; if ( t . length ) return t ; }
// 扩 展 : 允 许 后 端 将 token 放 在 data . session . token
NSDictionary * d2 = dict [ @ "data" ] ; if ( [ d2 isKindOfClass : NSDictionary . class ] ) {
NSDictionary * session = d2 [ @ "session" ] ; if ( [ session isKindOfClass : NSDictionary . class ] ) { t = pick ( session ) ; if ( t . length ) return t ; }
}
return nil ;
}
@ end