Files
keyboard/keyBoard/Class/Manager/AppleSignInManager.m

140 lines
5.6 KiB
Mathematica
Raw Normal View History

2025-10-30 14:29:11 +08:00
// AppleSignInManager.m
// Apple
#import "AppleSignInManager.h"
#import <UIKit/UIKit.h>
#import <Security/Security.h>
static NSString * const kKBAppleUserIdentifierKey = @"com.company.keyboard.apple.user"; //
@interface AppleSignInManager ()
@property (nonatomic, weak) UIViewController *presentingVC;
@property (nonatomic, copy) KBAppleSignInCompletion completion;
@end
@implementation AppleSignInManager
+ (instancetype)shared {
static AppleSignInManager *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ instance = [AppleSignInManager new]; });
return instance;
}
- (NSString *)storedUserIdentifier {
return [self.class keychainLoad:kKBAppleUserIdentifierKey];
}
- (void)signInFromViewController:(UIViewController *)presenting completion:(KBAppleSignInCompletion)completion {
if (@available(iOS 13.0, *)) {
self.presentingVC = presenting;
self.completion = completion;
ASAuthorizationAppleIDProvider *provider = [ASAuthorizationAppleIDProvider new];
ASAuthorizationAppleIDRequest *request = provider.createRequest;
request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
ASAuthorizationController *controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
controller.delegate = self;
controller.presentationContextProvider = self;
[controller performRequests];
} else {
if (completion) {
NSError *err = [NSError errorWithDomain:@"AppleSignIn" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Apple 登录需要 iOS 13 及以上版本"}];
completion(nil, err);
}
}
}
- (void)checkCredentialStateWithCompletion:(void(^)(ASAuthorizationAppleIDProviderCredentialState state))completion {
if (!completion) return;
if (@available(iOS 13.0, *)) {
NSString *userID = self.storedUserIdentifier;
if (!userID) {
completion(ASAuthorizationAppleIDProviderCredentialNotFound);
return;
}
ASAuthorizationAppleIDProvider *provider = [ASAuthorizationAppleIDProvider new];
[provider getCredentialStateForUserID:userID completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{ completion(credentialState); });
}];
} else {
completion(ASAuthorizationAppleIDProviderCredentialNotFound);
}
}
#pragma mark - (ASAuthorizationControllerDelegate)
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) {
if (@available(iOS 13.0, *)) {
ASAuthorizationAppleIDCredential *credential = authorization.credential;
// userIdentifier便
NSString *userID = credential.user;
[self.class keychainSave:kKBAppleUserIdentifierKey value:userID];
if (self.completion) {
self.completion(credential, nil);
}
}
self.completion = nil;
self.presentingVC = nil;
}
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) {
if (self.completion) {
self.completion(nil, error);
}
self.completion = nil;
self.presentingVC = nil;
}
#pragma mark - (ASAuthorizationControllerPresentationContextProviding)
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)) {
return self.presentingVC.view.window ?: UIApplication.sharedApplication.keyWindow;
}
#pragma mark - Keychain
// userIdentifier使 App
- (void)signOut {
NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: kKBAppleUserIdentifierKey,
(__bridge id)kSecAttrAccount: kKBAppleUserIdentifierKey};
SecItemDelete((__bridge CFDictionaryRef)query);
}
+ (BOOL)keychainSave:(NSString *)key value:(NSString *)value {
if (!key) return NO;
NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: key,
(__bridge id)kSecAttrAccount: key};
SecItemDelete((__bridge CFDictionaryRef)query);
NSMutableDictionary *attributes = [query mutableCopy];
attributes[(__bridge id)kSecValueData] = data ?: [NSData data];
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
return (status == errSecSuccess);
}
+ (NSString *)keychainLoad:(NSString *)key {
if (!key) return nil;
NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: key,
(__bridge id)kSecAttrAccount: key,
(__bridge id)kSecReturnData: @YES,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne};
CFTypeRef dataRef = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataRef);
if (status != errSecSuccess || !dataRef) return nil;
NSData *data = (__bridge_transfer NSData *)dataRef;
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
@end