2025-10-30 14:29:11 +08:00
|
|
|
|
// LoginViewController.m
|
|
|
|
|
|
|
|
|
|
|
|
#import "LoginViewController.h"
|
|
|
|
|
|
#import "AppleSignInManager.h"
|
|
|
|
|
|
#import <AuthenticationServices/AuthenticationServices.h>
|
|
|
|
|
|
#import <Masonry/Masonry.h>
|
2025-10-31 16:06:54 +08:00
|
|
|
|
#import "KBAuthManager.h"
|
2025-10-30 14:29:11 +08:00
|
|
|
|
|
|
|
|
|
|
@interface LoginViewController ()
|
|
|
|
|
|
// 容器视图(用于居中摆放内容)
|
|
|
|
|
|
@property (nonatomic, strong) UIView *contentView;
|
|
|
|
|
|
// 标题标签(懒加载)
|
|
|
|
|
|
@property (nonatomic, strong) UILabel *titleLabel;
|
|
|
|
|
|
// Apple 登录按钮容器(懒加载)
|
|
|
|
|
|
@property (nonatomic, strong) UIView *appleContainer;
|
2025-11-03 20:02:11 +08:00
|
|
|
|
// Apple 登录按钮(懒加载)
|
2025-10-30 14:29:11 +08:00
|
|
|
|
@property (nonatomic, strong) ASAuthorizationAppleIDButton *appleButton API_AVAILABLE(ios(13.0));
|
|
|
|
|
|
// iOS13 以下的占位按钮(懒加载)
|
|
|
|
|
|
@property (nonatomic, strong) UIButton *compatHintButton;
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation LoginViewController
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewDidLoad {
|
|
|
|
|
|
[super viewDidLoad];
|
|
|
|
|
|
if (@available(iOS 13.0, *)) {
|
|
|
|
|
|
self.view.backgroundColor = [UIColor systemBackgroundColor];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.view.backgroundColor = [UIColor whiteColor];
|
|
|
|
|
|
}
|
|
|
|
|
|
[self setupUI]; // 仅做布局,控件懒加载
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)setupUI {
|
|
|
|
|
|
// 添加并约束 contentView
|
|
|
|
|
|
[self.view addSubview:self.contentView];
|
|
|
|
|
|
[self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
|
|
|
make.center.equalTo(self.view);
|
|
|
|
|
|
make.left.equalTo(self.view).offset(24);
|
|
|
|
|
|
make.right.equalTo(self.view).offset(-24);
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
// 添加并约束 titleLabel
|
|
|
|
|
|
[self.contentView addSubview:self.titleLabel];
|
|
|
|
|
|
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
|
|
|
make.top.left.right.equalTo(self.contentView);
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
// 添加并约束 appleContainer
|
|
|
|
|
|
[self.contentView addSubview:self.appleContainer];
|
|
|
|
|
|
[self.appleContainer mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
|
|
|
make.top.equalTo(self.titleLabel.mas_bottom).offset(24);
|
|
|
|
|
|
make.left.right.equalTo(self.contentView);
|
|
|
|
|
|
make.height.mas_equalTo(50);
|
|
|
|
|
|
make.bottom.equalTo(self.contentView);
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 懒加载:容器视图
|
|
|
|
|
|
- (UIView *)contentView {
|
|
|
|
|
|
if (!_contentView) {
|
|
|
|
|
|
_contentView = [UIView new];
|
|
|
|
|
|
}
|
|
|
|
|
|
return _contentView;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 懒加载:标题
|
|
|
|
|
|
- (UILabel *)titleLabel {
|
|
|
|
|
|
if (!_titleLabel) {
|
|
|
|
|
|
_titleLabel = [UILabel new];
|
|
|
|
|
|
_titleLabel.text = @"登录";
|
|
|
|
|
|
_titleLabel.font = [UIFont boldSystemFontOfSize:24];
|
|
|
|
|
|
_titleLabel.textAlignment = NSTextAlignmentCenter;
|
|
|
|
|
|
}
|
|
|
|
|
|
return _titleLabel;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 懒加载:Apple 按钮容器(内部放按钮或占位提示)
|
|
|
|
|
|
- (UIView *)appleContainer {
|
|
|
|
|
|
if (!_appleContainer) {
|
|
|
|
|
|
_appleContainer = [UIView new];
|
|
|
|
|
|
if (@available(iOS 13.0, *)) {
|
|
|
|
|
|
[_appleContainer addSubview:self.appleButton];
|
|
|
|
|
|
[self.appleButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
|
|
|
make.edges.equalTo(_appleContainer);
|
|
|
|
|
|
}];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
[_appleContainer addSubview:self.compatHintButton];
|
|
|
|
|
|
[self.compatHintButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
|
|
|
make.edges.equalTo(_appleContainer);
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return _appleContainer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 懒加载:Apple 登录按钮(iOS13+)
|
|
|
|
|
|
- (ASAuthorizationAppleIDButton *)appleButton API_AVAILABLE(ios(13.0)) {
|
|
|
|
|
|
if (!_appleButton) {
|
|
|
|
|
|
_appleButton = [ASAuthorizationAppleIDButton new];
|
|
|
|
|
|
[_appleButton addTarget:self action:@selector(handleAppleIDButtonPress) forControlEvents:UIControlEventTouchUpInside];
|
|
|
|
|
|
}
|
|
|
|
|
|
return _appleButton;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 懒加载:iOS13 以下占位提示
|
|
|
|
|
|
- (UIButton *)compatHintButton {
|
|
|
|
|
|
if (!_compatHintButton) {
|
|
|
|
|
|
_compatHintButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
|
|
|
|
[_compatHintButton setTitle:@"需要 iOS13+ 才能使用 Apple 登录" forState:UIControlStateNormal];
|
|
|
|
|
|
_compatHintButton.enabled = NO;
|
|
|
|
|
|
}
|
|
|
|
|
|
return _compatHintButton;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)handleAppleIDButtonPress API_AVAILABLE(ios(13.0)) {
|
2025-11-10 15:38:30 +08:00
|
|
|
|
KBWeakSelf
|
2025-10-30 14:29:11 +08:00
|
|
|
|
[[AppleSignInManager shared] signInFromViewController:self completion:^(ASAuthorizationAppleIDCredential * _Nullable credential, NSError * _Nullable error) {
|
|
|
|
|
|
__strong typeof(weakSelf) selfStrong = weakSelf;
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
if (selfStrong.onLoginFailure) selfStrong.onLoginFailure(error);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!credential) return;
|
|
|
|
|
|
|
|
|
|
|
|
NSString *userID = credential.user ?: @"";
|
|
|
|
|
|
NSString *email = credential.email ?: @""; // 仅在首次授权时返回
|
|
|
|
|
|
NSPersonNameComponents *name = credential.fullName; // 同上,仅首次返回
|
|
|
|
|
|
NSString *givenName = name.givenName ?: @"";
|
|
|
|
|
|
NSString *familyName = name.familyName ?: @"";
|
|
|
|
|
|
|
|
|
|
|
|
NSMutableDictionary *info = [@{ @"userIdentifier": userID } mutableCopy];
|
|
|
|
|
|
if (email.length) info[@"email"] = email;
|
|
|
|
|
|
if (givenName.length || familyName.length) {
|
|
|
|
|
|
info[@"givenName"] = givenName;
|
|
|
|
|
|
info[@"familyName"] = familyName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如需服务端校验,附带 identityToken / authorizationCode
|
|
|
|
|
|
NSData *tokenData = credential.identityToken;
|
|
|
|
|
|
if (tokenData) {
|
|
|
|
|
|
NSString *token = [[NSString alloc] initWithData:tokenData encoding:NSUTF8StringEncoding] ?: @"";
|
|
|
|
|
|
if (token.length) info[@"identityToken"] = token;
|
|
|
|
|
|
}
|
|
|
|
|
|
NSData *codeData = credential.authorizationCode;
|
|
|
|
|
|
if (codeData) {
|
|
|
|
|
|
NSString *code = [[NSString alloc] initWithData:codeData encoding:NSUTF8StringEncoding] ?: @"";
|
|
|
|
|
|
if (code.length) info[@"authorizationCode"] = code;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 16:06:54 +08:00
|
|
|
|
// 将示例中的 identityToken 暂存为访问令牌(实际项目应调用服务端换取业务 token)
|
|
|
|
|
|
NSString *accessToken = info[@"identityToken"];
|
|
|
|
|
|
NSString *uid = info[@"userIdentifier"]; // 不变
|
|
|
|
|
|
if (accessToken.length > 0) {
|
|
|
|
|
|
[[KBAuthManager shared] saveAccessToken:accessToken refreshToken:nil expiryDate:nil userIdentifier:uid];
|
|
|
|
|
|
}
|
2025-10-30 14:29:11 +08:00
|
|
|
|
if (selfStrong.onLoginSuccess) selfStrong.onLoginSuccess(info);
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@end
|