From 85a3694e353d92c780f3407e778883cf4c20a6a2 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Thu, 30 Oct 2025 14:29:11 +0800 Subject: [PATCH] apple login --- keyBoard.xcodeproj/project.pbxproj | 32 +++- keyBoard/AppDelegate.m | 4 +- keyBoard/Class/Login/VC/LoginViewController.h | 18 ++ keyBoard/Class/Login/VC/LoginViewController.m | 154 ++++++++++++++++++ keyBoard/Class/Manager/AppleSignInManager.h | 37 +++++ keyBoard/Class/Manager/AppleSignInManager.m | 139 ++++++++++++++++ keyBoard/keyBoard.entitlements | 10 ++ 7 files changed, 388 insertions(+), 6 deletions(-) create mode 100644 keyBoard/Class/Login/VC/LoginViewController.h create mode 100644 keyBoard/Class/Login/VC/LoginViewController.m create mode 100644 keyBoard/Class/Manager/AppleSignInManager.h create mode 100644 keyBoard/Class/Manager/AppleSignInManager.m create mode 100644 keyBoard/keyBoard.entitlements diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 58c917b..67f4203 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -34,6 +34,8 @@ 04FC95DD2EB202A3007BD342 /* KBGuideVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95DC2EB202A3007BD342 /* KBGuideVC.m */; }; 04FC95E52EB220B5007BD342 /* UIColor+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95E42EB220B5007BD342 /* UIColor+Extension.m */; }; 04FC95E92EB23B67007BD342 /* KBNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95E72EB23B67007BD342 /* KBNetworkManager.m */; }; + 04FC95F12EB339A7007BD342 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95F02EB339A7007BD342 /* LoginViewController.m */; }; + 04FC95F42EB339C1007BD342 /* AppleSignInManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95F32EB339C1007BD342 /* AppleSignInManager.m */; }; 04FC97002EB30A00007BD342 /* KBGuideTopCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC96FF2EB30A00007BD342 /* KBGuideTopCell.m */; }; 04FC97032EB30A00007BD342 /* KBGuideKFCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97022EB30A00007BD342 /* KBGuideKFCell.m */; }; 04FC97062EB30A00007BD342 /* KBGuideUserCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97052EB30A00007BD342 /* KBGuideUserCell.m */; }; @@ -124,6 +126,11 @@ 04FC95E42EB220B5007BD342 /* UIColor+Extension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIColor+Extension.m"; sourceTree = ""; }; 04FC95E62EB23B67007BD342 /* KBNetworkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBNetworkManager.h; sourceTree = ""; }; 04FC95E72EB23B67007BD342 /* KBNetworkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBNetworkManager.m; sourceTree = ""; }; + 04FC95EF2EB339A7007BD342 /* LoginViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginViewController.h; sourceTree = ""; }; + 04FC95F02EB339A7007BD342 /* LoginViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginViewController.m; sourceTree = ""; }; + 04FC95F22EB339C1007BD342 /* AppleSignInManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppleSignInManager.h; sourceTree = ""; }; + 04FC95F32EB339C1007BD342 /* AppleSignInManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppleSignInManager.m; sourceTree = ""; }; + 04FC95F52EB33B52007BD342 /* keyBoard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = keyBoard.entitlements; sourceTree = ""; }; 04FC96FE2EB30A00007BD342 /* KBGuideTopCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBGuideTopCell.h; sourceTree = ""; }; 04FC96FF2EB30A00007BD342 /* KBGuideTopCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBGuideTopCell.m; sourceTree = ""; }; 04FC97012EB30A00007BD342 /* KBGuideKFCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBGuideKFCell.h; sourceTree = ""; }; @@ -172,6 +179,7 @@ 04C6EAB92EAF86530089C901 /* keyBoard */ = { isa = PBXGroup; children = ( + 04FC95F52EB33B52007BD342 /* keyBoard.entitlements */, 04FC95BF2EB1E3B1007BD342 /* Class */, 04C6EAE32EAF942E0089C901 /* VC */, 04C6EAAC2EAF86530089C901 /* AppDelegate.h */, @@ -347,6 +355,7 @@ 04FC95BF2EB1E3B1007BD342 /* Class */ = { isa = PBXGroup; children = ( + 04FC95EE2EB3399D007BD342 /* Manager */, 04FC95ED2EB33611007BD342 /* Login */, 04FC95E82EB23B67007BD342 /* Network */, 04FC95E22EB2208F007BD342 /* Categories */, @@ -476,6 +485,8 @@ 04FC95EC2EB33611007BD342 /* VC */ = { isa = PBXGroup; children = ( + 04FC95EF2EB339A7007BD342 /* LoginViewController.h */, + 04FC95F02EB339A7007BD342 /* LoginViewController.m */, ); path = VC; sourceTree = ""; @@ -490,6 +501,15 @@ path = Login; sourceTree = ""; }; + 04FC95EE2EB3399D007BD342 /* Manager */ = { + isa = PBXGroup; + children = ( + 04FC95F22EB339C1007BD342 /* AppleSignInManager.h */, + 04FC95F32EB339C1007BD342 /* AppleSignInManager.m */, + ); + path = Manager; + sourceTree = ""; + }; 2C53A0856097DCFBE7B55649 /* Pods */ = { isa = PBXGroup; children = ( @@ -735,6 +755,7 @@ 04FC95D22EB1E7AE007BD342 /* MyVC.m in Sources */, 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */, 04C6EABE2EAF86530089C901 /* AppDelegate.m in Sources */, + 04FC95F12EB339A7007BD342 /* LoginViewController.m in Sources */, 04FC95D72EB1EA16007BD342 /* BaseTableView.m in Sources */, 04FC95D82EB1EA16007BD342 /* BaseCell.m in Sources */, 04FC95C92EB1E4C9007BD342 /* BaseNavigationController.m in Sources */, @@ -749,6 +770,7 @@ 04FC95CF2EB1E7A1007BD342 /* HomeVC.m in Sources */, 04C6EABF2EAF86530089C901 /* main.m in Sources */, 04FC95CC2EB1E780007BD342 /* BaseTabBarController.m in Sources */, + 04FC95F42EB339C1007BD342 /* AppleSignInManager.m in Sources */, 04C6EAC12EAF86530089C901 /* ViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -803,7 +825,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.keyBoard.CustomKeyboard; + PRODUCT_BUNDLE_IDENTIFIER = com.keyBoardst.CustomKeyboard; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -831,7 +853,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.keyBoard.CustomKeyboard; + PRODUCT_BUNDLE_IDENTIFIER = com.keyBoardst.CustomKeyboard; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -845,6 +867,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = keyBoard/keyBoard.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = TN6HHV45BB; @@ -863,7 +886,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.keyBoard; + PRODUCT_BUNDLE_IDENTIFIER = com.keyBoardst; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -877,6 +900,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = keyBoard/keyBoard.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = TN6HHV45BB; @@ -895,7 +919,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.keyBoard; + PRODUCT_BUNDLE_IDENTIFIER = com.keyBoardst; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/keyBoard/AppDelegate.m b/keyBoard/AppDelegate.m index 278d878..471d3d5 100644 --- a/keyBoard/AppDelegate.m +++ b/keyBoard/AppDelegate.m @@ -6,11 +6,11 @@ // #import "AppDelegate.h" -#import "ViewController.h" #import "KBPermissionViewController.h" #import #import #import "BaseTabBarController.h" +#import "LoginViewController.h" static NSString * const kKBKeyboardExtensionBundleId = @"com.keyBoard.CustomKeyboard"; @@ -41,7 +41,7 @@ static NSString * const kKBKeyboardExtensionBundleId = @"com.keyBoard.CustomKeyb self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; - BaseTabBarController *vc = [[BaseTabBarController alloc] init]; + LoginViewController *vc = [[LoginViewController alloc] init]; self.window.rootViewController = vc; } diff --git a/keyBoard/Class/Login/VC/LoginViewController.h b/keyBoard/Class/Login/VC/LoginViewController.h new file mode 100644 index 0000000..3c00c1f --- /dev/null +++ b/keyBoard/Class/Login/VC/LoginViewController.h @@ -0,0 +1,18 @@ +// LoginViewController.h +// 仅包含“Apple 登录”的登录页 + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface LoginViewController : UIViewController + +/// Apple 登录成功回调。包含 userIdentifier;首次授权可能包含 email/fullName。 +@property (nonatomic, copy, nullable) void (^onLoginSuccess)(NSDictionary *userInfo); + +/// 登录失败或取消时回调(返回错误)。 +@property (nonatomic, copy, nullable) void (^onLoginFailure)(NSError *error); + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Login/VC/LoginViewController.m b/keyBoard/Class/Login/VC/LoginViewController.m new file mode 100644 index 0000000..28c6ab1 --- /dev/null +++ b/keyBoard/Class/Login/VC/LoginViewController.m @@ -0,0 +1,154 @@ +// LoginViewController.m + +#import "LoginViewController.h" +#import "AppleSignInManager.h" +#import +#import + +@interface LoginViewController () +// 容器视图(用于居中摆放内容) +@property (nonatomic, strong) UIView *contentView; +// 标题标签(懒加载) +@property (nonatomic, strong) UILabel *titleLabel; +// Apple 登录按钮容器(懒加载) +@property (nonatomic, strong) UIView *appleContainer; +// iOS13+ 的 Apple 登录按钮(懒加载) +@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)) { + __weak typeof(self) weakSelf = self; + [[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; + } + + if (selfStrong.onLoginSuccess) selfStrong.onLoginSuccess(info); + }]; +} + +@end diff --git a/keyBoard/Class/Manager/AppleSignInManager.h b/keyBoard/Class/Manager/AppleSignInManager.h new file mode 100644 index 0000000..5c2b73d --- /dev/null +++ b/keyBoard/Class/Manager/AppleSignInManager.h @@ -0,0 +1,37 @@ +// AppleSignInManager.h +// 封装“用 Apple 登录”逻辑 + +#import +#import +@class UIViewController; + +NS_ASSUME_NONNULL_BEGIN + +/// Apple 登录回调结果 +typedef void (^KBAppleSignInCompletion)(ASAuthorizationAppleIDCredential * _Nullable credential, NSError * _Nullable error); + +/// AppleSignInManager 集中管理 Apple 登录流程与代理回调 +@interface AppleSignInManager : NSObject + +/// 单例便捷访问 ++ (instancetype)shared; + +/// 发起 Apple 登录 +/// - 参数说明: +/// - presenting: 用于呈现授权界面的控制器 +/// - completion: 主线程回调,返回凭证或错误 +- (void)signInFromViewController:(UIViewController *)presenting completion:(KBAppleSignInCompletion)completion; + +/// 根据已存储的 userIdentifier 检查凭证状态 +/// 返回:ASAuthorizationAppleIDProviderCredentialAuthorized/Revoked/NotFound +- (void)checkCredentialStateWithCompletion:(void(^)(ASAuthorizationAppleIDProviderCredentialState state))completion; + +/// 最近一次成功登录的 userIdentifier(已持久化) +@property (nonatomic, readonly, nullable) NSString *storedUserIdentifier; + +/// 本地登出:清除已存储的 userIdentifier(不会向苹果撤销授权) +- (void)signOut; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Manager/AppleSignInManager.m b/keyBoard/Class/Manager/AppleSignInManager.m new file mode 100644 index 0000000..1cb02b7 --- /dev/null +++ b/keyBoard/Class/Manager/AppleSignInManager.m @@ -0,0 +1,139 @@ +// AppleSignInManager.m +// 封装“用 Apple 登录”的实现与存储 + +#import "AppleSignInManager.h" +#import +#import + +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 diff --git a/keyBoard/keyBoard.entitlements b/keyBoard/keyBoard.entitlements new file mode 100644 index 0000000..a812db5 --- /dev/null +++ b/keyBoard/keyBoard.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.applesignin + + Default + + +