├── .DS_Store ├── DDYAuthManager ├── DDYAuthManagerExample │ ├── zh-Hans.lproj │ │ └── LaunchScreen.strings │ ├── .DS_Store │ ├── AppDelegate.h │ ├── main.m │ ├── ViewController.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── DDYAuthManagerInfo.plist │ └── ViewController.m ├── .DS_Store ├── DDYAuthManager │ ├── .DS_Store │ ├── NSBundle+DDYAuthManger.h │ ├── DDYAuthManager.bundle │ │ ├── zh-Hans.lproj │ │ │ └── DDYAuthManager.strings │ │ └── en.lproj │ │ │ └── DDYAuthManager.strings │ ├── NSBundle+DDYAuthManger.m │ ├── DDYAuthManager.h │ └── DDYAuthManager.m └── DDYAuthManager.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── project.pbxproj ├── DDYAuthManager.podspec ├── LICENSE ├── .gitignore └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainOpen/DDYAuthManager/HEAD/.DS_Store -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/zh-Hans.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /DDYAuthManager/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainOpen/DDYAuthManager/HEAD/DDYAuthManager/.DS_Store -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainOpen/DDYAuthManager/HEAD/DDYAuthManager/DDYAuthManager/.DS_Store -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainOpen/DDYAuthManager/HEAD/DDYAuthManager/DDYAuthManagerExample/.DS_Store -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | @end 8 | 9 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AppDelegate.h" 3 | 4 | int main(int argc, char * argv[]) { 5 | @autoreleasepool { 6 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/ViewController.h: -------------------------------------------------------------------------------- 1 | /** MARK: - DDYAuthManager 2018/10/22 2 | * !!!: Author: 豆电雨 3 | * !!!: QQ/WX: 634778311 4 | * !!!: Github: https://github.com/RainOpen/ 5 | * !!!: Blog: https://www.jianshu.com/u/a4bc2516e9e5 6 | * MARK: - 示例文件 7 | */ 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Latest 7 | PreviewsEnabled 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager/NSBundle+DDYAuthManger.h: -------------------------------------------------------------------------------- 1 | /// MARK: - DDYAuthManager 2018/10/22 2 | /// !!!: Author: 豆电雨 3 | /// !!!: QQ/WX: 634778311 4 | /// !!!: Github: https://github.com/RainOpen/ 5 | /// !!!: Blog: https://juejin.im/user/57dddcd8128fe10064cadee9 6 | /// MARK: - 用于自定义bundle资源加载(比如国际化文件、图片等) 7 | 8 | #import 9 | 10 | @interface NSBundle (DDYAuthManger) 11 | 12 | + (instancetype)ddyAuthManagerBundle; 13 | + (NSString *)ddyAuthManagerLocalizedStringForKey:(NSString *)key; 14 | + (NSString *)ddyAuthManagerLocalizedStringForKey:(NSString *)key value:(NSString *)value; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "ViewController.h" 3 | 4 | @interface AppDelegate () 5 | 6 | @end 7 | 8 | @implementation AppDelegate 9 | 10 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 11 | _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 12 | _window.backgroundColor = [UIColor whiteColor]; 13 | _window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 14 | [_window makeKeyAndVisible]; 15 | return YES; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager/DDYAuthManager.bundle/zh-Hans.lproj/DDYAuthManager.strings: -------------------------------------------------------------------------------- 1 | "DDYNoAuthMicrophone" = "请在 'Setting-%@' 选项中,允许访问你的麦克风"; 2 | "DDYNoAuthCamera" = "请在 'Setting-%@' 选项中,允许访问你的相机"; 3 | "DDYNoAuthAlbum" = "请在 'Setting-%@' 选项中,允许访问你的照片"; 4 | "DDYNoAuthContacts" = "请在 'Setting-%@' 选项中,允许访问你的通讯录"; 5 | "DDYNoAuthCalendars" = "请在 'Setting-%@' 选项中,允许访问你的日历"; 6 | "DDYNoAuthReminders" = "请在 'Setting-%@' 选项中,允许访问你的提醒事项"; 7 | "DDYNoAuthNetwork" = "请在 'Setting-%@' 选项中,允许访问你的网络"; 8 | "DDYNoAuthNotifications" = "请在 'Setting-%@' 选项中,允许访问你的通知"; 9 | "DDYNoAuthLocation" = "请在 'Setting-%@' 选项中,允许访问你的定位服务"; 10 | "DDYNoAuthSpeech" = "请在 'Setting-%@' 选项中,允许访问你的语音识别"; 11 | "DDYNoAuthHealth" = "请在 'Setting-%@' 选项中,允许访问你的健康"; 12 | 13 | "DDYNoAuthCancel" = "取消"; 14 | "DDYNoAuthConfirm" = "确定"; 15 | -------------------------------------------------------------------------------- /DDYAuthManager.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |ddyspec| 2 | ddyspec.name = 'DDYAuthManager' 3 | ddyspec.version = '1.4.0' 4 | ddyspec.summary = '各种权限验证(部分带主动申请权限),麦克风/相机/相册/日历/备忘录/联网/推送通知/定位/语音识别权限等等' 5 | ddyspec.homepage = 'https://github.com/RainOpen/DDYAuthManager' 6 | ddyspec.license = 'MIT' 7 | ddyspec.authors = {'Rain' => '634778311@qq.com'} 8 | ddyspec.platform = :ios, '8.0' 9 | ddyspec.source = {:git => 'https://github.com/RainOpen/DDYAuthManager.git', :tag => ddyspec.version} 10 | ddyspec.source_files = 'DDYAuthManager/DDYAuthManager/*.{h,m}' 11 | ddyspec.resource = 'DDYAuthManager/DDYAuthManager/DDYAuthManager.bundle' 12 | ddyspec.requires_arc = true 13 | ddyspec.frameworks = "UIKit", "AVFoundation", "AssetsLibrary", "Photos", "AddressBook", "Contacts", "EventKit", "CoreTelephony", "UserNotifications", "CoreLocation", "Speech", "LocalAuthentication" 14 | end 15 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager/DDYAuthManager.bundle/en.lproj/DDYAuthManager.strings: -------------------------------------------------------------------------------- 1 | "DDYNoAuthMicrophone" = "Allow access to the Microphone on 'Setting-%@'"; 2 | "DDYNoAuthCamera" = "Allow access to the Camera on 'Setting-%@'"; 3 | "DDYNoAuthAlbum" = "Allow access to the Photos on 'Setting-%@'"; 4 | "DDYNoAuthContacts" = "Allow access to the Contacts on 'Setting-%@'"; 5 | "DDYNoAuthCalendars" = "Allow access to the Calendars on 'Setting-%@'"; 6 | "DDYNoAuthReminders" = "Allow access to the Reminders on 'Setting-%@'"; 7 | "DDYNoAuthNetwork" = "Allow access to the Network on 'Setting-%@'"; 8 | "DDYNoAuthNotifications" = "Allow access to the Notifications on 'Setting-%@'"; 9 | "DDYNoAuthLocation" = "Allow access to the Location services on 'Setting-%@'"; 10 | "DDYNoAuthSpeech" = "Allow access to the Speech on 'Setting-%@'"; 11 | "DDYNoAuthHealth" = "Allow access to the Health on 'Setting-%@'"; 12 | 13 | "DDYNoAuthCancel" = "Cancel"; 14 | "DDYNoAuthConfirm" = "OK"; 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 RainOpen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots/**/*.png 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | DDYAuthManager/.DS_Store 65 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager/NSBundle+DDYAuthManger.m: -------------------------------------------------------------------------------- 1 | /// MARK: - DDYAuthManager 2018/10/22 2 | /// !!!: Author: 豆电雨 3 | /// !!!: QQ/WX: 634778311 4 | /// !!!: Github: https://github.com/RainOpen/ 5 | /// !!!: Blog: https://juejin.im/user/57dddcd8128fe10064cadee9 6 | /// MARK: - NSBundle+DDYAuthManger.m 7 | 8 | #import "NSBundle+DDYAuthManger.h" 9 | #import "DDYAuthManager.h" 10 | 11 | @implementation NSBundle (DDYAuthManger) 12 | 13 | + (instancetype)ddyAuthManagerBundle { 14 | NSBundle *bundle = [NSBundle bundleForClass:[DDYAuthManager class]]; 15 | return [NSBundle bundleWithURL:[bundle URLForResource:@"DDYAuthManager" withExtension:@"bundle"]]; 16 | } 17 | 18 | + (NSString *)ddyAuthManagerLocalizedStringForKey:(NSString *)key { 19 | return [self ddyAuthManagerLocalizedStringForKey:key value:nil]; 20 | } 21 | 22 | + (NSString *)ddyAuthManagerLocalizedStringForKey:(NSString *)key value:(NSString *)value { 23 | NSString *language = [[NSUserDefaults standardUserDefaults] objectForKey:@"DDYLanguages"]; 24 | if (!language) { 25 | language = [NSLocale preferredLanguages].firstObject; 26 | } 27 | if (!language) { 28 | language = @"zh-Hans"; 29 | } else if ([language hasPrefix:@"zh"]) { 30 | language = @"zh-Hans"; 31 | } else { 32 | language = @"en"; 33 | } 34 | NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle ddyAuthManagerBundle] pathForResource:language ofType:@"lproj"]]; 35 | if (bundle) { 36 | // 如果默认Localizable.strings则nil,这里命名DDYAuthManager.strings 37 | value = [bundle localizedStringForKey:key value:value table:@"DDYAuthManager"]; 38 | } 39 | // (如果拖入工程)可以在[NSBundle mainBundle]查找,如果没有则返回原key 40 | return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/DDYAuthManagerInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NFCReaderUsageDescription 24 | App需要您的同意,才能使用NFC,是否同意? 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | NSAppleMusicUsageDescription 31 | App需要您的同意,才能访问媒体资料库,是否同意? 32 | NSBluetoothPeripheralUsageDescription 33 | App需要您的同意,才能访问蓝牙,是否同意? 34 | NSCalendarsUsageDescription 35 | App需要您的同意,才能访问日历,是否同意? 36 | NSCameraUsageDescription 37 | App需要您的同意,才能使用相机,是否同意? 38 | NSContactsUsageDescription 39 | App需要您的同意,才能使用通讯录,是否同意? 40 | NSFaceIDUsageDescription 41 | App需要您的同意,才能使用Face ID,是否同意? 42 | NSHealthShareUsageDescription 43 | App需要您的同意,才能访问健康分享,是否同意? 44 | NSHealthUpdateUsageDescription 45 | App需要您的同意,才能访问健康更新,是否同意? 46 | NSHomeKitUsageDescription 47 | App需要您的同意,才能使用HomeKit,是否同意? 48 | NSLocationAlwaysAndWhenInUseUsageDescription 49 | App需要您的同意,才能访问位置 50 | NSLocationAlwaysUsageDescription 51 | App需要您的同意,才能始终访问位置 52 | NSLocationUsageDescription 53 | App需要您的同意,才能访问位置,是否同意? 54 | NSLocationWhenInUseUsageDescription 55 | App需要您的同意,才能在使用期间访问位置 56 | NSMicrophoneUsageDescription 57 | App需要您的同意,才能使用麦克风,是否同意? 58 | NSMotionUsageDescription 59 | App需要您的同意,才能访问运动与健身,是否同意? 60 | NSPhotoLibraryAddUsageDescription 61 | App需要您的同意,才能保存图片到相册,是否同意? 62 | NSPhotoLibraryUsageDescription 63 | App需要您的同意,才能访问相册,是否同意? 64 | NSRemindersUsageDescription 65 | App需要您的同意,才能访问提醒事项,是否同意? 66 | NSSiriUsageDescription 67 | App需要您的同意,才能使用Siri,是否同意? 68 | NSSpeechRecognitionUsageDescription 69 | App需要您的同意,才能使用语音识别,是否同意? 70 | UIBackgroundModes 71 | 72 | location 73 | remote-notification 74 | 75 | UILaunchStoryboardName 76 | LaunchScreen 77 | UIRequiredDeviceCapabilities 78 | 79 | armv7 80 | 81 | UISupportedInterfaceOrientations 82 | 83 | UIInterfaceOrientationPortrait 84 | 85 | UISupportedInterfaceOrientations~ipad 86 | 87 | UIInterfaceOrientationPortrait 88 | UIInterfaceOrientationPortraitUpsideDown 89 | UIInterfaceOrientationLandscapeLeft 90 | UIInterfaceOrientationLandscapeRight 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager/DDYAuthManager.h: -------------------------------------------------------------------------------- 1 | /// MARK: - DDYAuthManager 2018/10/22 2 | /// !!!: Author: 豆电雨 3 | /// !!!: QQ/WX: 634778311 4 | /// !!!: Github: https://github.com/RainOpen/ 5 | /// !!!: Blog: https://juejin.im/user/57dddcd8128fe10064cadee9 6 | /// MARK: - 为了更直观,所以将成功与失败回调block分开了 7 | 8 | #import 9 | #import 10 | // 麦克风/摄像头(相机)权限使用 11 | #import 12 | // 相册权限使用 13 | #import // iOS 6-9(可能要删除) 14 | #import // iOS 8+ 15 | // 通讯录权限使用 16 | #import // iOS 9- 17 | #import // iOS 9+ 18 | // 联网权限使用 19 | #import 20 | // 通知权限使用 21 | #import 22 | // 定位权限使用 23 | #import 24 | 25 | /// 通讯录兼容(iOS 9- 和 iOS 9+)权限状态 26 | typedef NS_ENUM(NSInteger, DDYContactsAuthStatus) { 27 | DDYContactsAuthStatusNotDetermined = 0, 28 | DDYContactsAuthStatusRestricted = 1, 29 | DDYContactsAuthStatusDenied = 2, 30 | DDYContactsAuthStatusAuthorized = 3, 31 | }; 32 | 33 | /// 推送通知兼容(iOS 10+ 和 iOS 8+)权限状态 34 | typedef NS_OPTIONS(NSUInteger, DDYPushNotiType) { 35 | DDYPushNotiTypeBadge = (1 << 0), 36 | DDYPushNotiTypeSound = (1 << 1), 37 | DDYPushNotiTypeAlert = (1 << 2), 38 | }; 39 | 40 | /// 定位 只允许鉴定两种状态 41 | typedef NS_ENUM(NSUInteger, DDYCLLocationType) { 42 | DDYCLLocationTypeAlways = 3, // 严格意义,如果不是always则失败并返回授权状态 43 | DDYCLLocationTypeInUse = 4, // 严格意义,如果不是InUse则失败并返回授权状态 44 | DDYCLLocationTypeAuthorized = 5, // 宽松意义,只要授权则成功并则失败并返回授权状态(极力推荐使用) 45 | }; 46 | 47 | @interface DDYAuthManager : NSObject 48 | /// 默认提示App名字 49 | @property (nonatomic, strong) NSString *appName; 50 | 51 | /// 麦克风权限 带主动请权 52 | /// @param show 无权限时默认提示 53 | /// @param success 已授权回调 54 | /// @param fail 无权限回调 55 | + (void)ddy_AudioAuthAlertShow:(BOOL)show 56 | success:(void (^)(void))success 57 | fail:(void (^)(AVAuthorizationStatus authStatus))fail; 58 | 59 | /// 摄像头(相机)权限 带主动请权 使用前最好先判断摄像头是否可用 60 | /// @param show 无权限时默认提示 61 | /// @param success 已授权回调 62 | /// @param fail 无权限回调 63 | + (void)ddy_CameraAuthAlertShow:(BOOL)show 64 | success:(void (^)(void))success 65 | fail:(void (^)(AVAuthorizationStatus authStatus))fail; 66 | 67 | /// 判断设备摄像头是否可用 68 | + (BOOL)isCameraAvailable; 69 | 70 | /// 前面的摄像头是否可用 71 | + (BOOL)isFrontCameraAvailable; 72 | 73 | /// 后面的摄像头是否可用 74 | + (BOOL)isRearCameraAvailable; 75 | 76 | /// 相册权限 iOS 8+ 带主动请权 77 | /// @param show 无权限时默认提示 78 | /// @param success 已授权回调 79 | /// @param fail 无权限回调 80 | + (void)ddy_AlbumAuthAlertShow:(BOOL)show 81 | success:(void (^)(void))success 82 | fail:(void (^)(PHAuthorizationStatus authStatus))fail; 83 | 84 | /// 通讯录权限 带主动请权 85 | /// @param show 无权限时默认提示 86 | /// @param success 已授权回调 87 | /// @param fail 无权限回调 88 | + (void)ddy_ContactsAuthAlertShow:(BOOL)show 89 | success:(void (^)(void))success 90 | fail:(void (^)(DDYContactsAuthStatus authStatus))fail; 91 | 92 | /// 用网络请求方式主动获取一次权限 首次安装才可能联网权限弹窗(如果弹窗时没点击任何信息直接关机,下次启动仍视为首次) 93 | /// @param url 要请求的URL 如:www.baidu.com http需设置AST 94 | + (void)ddy_GetNetAuthWithURL:(NSURL *)url; 95 | 96 | /// 联网权限 iOS 10+ 不带主动请权 http需设置AST 97 | /// @param show 无权限时默认提示 98 | /// @param success 已授权回调 99 | /// @param fail 无权限回调 100 | + (void)ddy_NetAuthAlertShow:(BOOL)show 101 | success:(void (^)(void))success 102 | fail:(void (^)(CTCellularDataRestrictedState authStatus))fail API_AVAILABLE(ios(10.0)); 103 | 104 | /// 推送通知权限 需要在打开 target -> Capabilities —> Push Notifications iOS10+带主动请权,iOS8-iOS9不带主动请权 105 | /// @param show 无权限时默认提示 106 | /// @param success 已授权回调 107 | /// @param fail 无权限回调 只返回是否有权限推送,不返回具体权限(badge,alert,sound) 108 | 109 | + (void)ddy_PushNotificationAuthAlertShow:(BOOL)show 110 | success:(void (^)(void))success 111 | fail:(void (^)(void))fail; 112 | 113 | /// 定位权限 不带主动请权 使用前先判断服务是否开启 [CLLocationManager locationServicesEnabled] 114 | /// @param type 定位类型 声明:Always可时时定位(包括后台时),若不需要不建议添加,可能影响上线审核,可做一些声明描述:GPS在后台持续运行,可能降低电池的寿命。 115 | /// @param show 无权限时默认提示 116 | /// @param success 已授权 117 | /// @param fail 未授权 如果是kCLAuthorizationStatusNotDetermined未经弹框(或者弹框未任何操作,例如弹框时关机),则需特殊处理 118 | + (void)ddy_LocationAuthType:(DDYCLLocationType)type 119 | alertShow:(BOOL)show 120 | success:(void (^)(void))success 121 | fail:(void (^)(CLAuthorizationStatus authStatus))fail; 122 | 123 | @end 124 | 125 | /** 126 | 127 | NSAppTransportSecurity 128 | 129 | NSAllowsArbitraryLoads 130 | 131 | 132 | 133 | 134 | 135 | NSAppleMusicUsageDescription 136 | App需要您的同意,才能访问媒体资料库,是否同意? 137 | NSBluetoothPeripheralUsageDescription 138 | App需要您的同意,才能访问蓝牙,是否同意? 139 | NSCalendarsUsageDescription 140 | App需要您的同意,才能访问日历,是否同意? 141 | NSCameraUsageDescription 142 | App需要您的同意,才能使用相机,是否同意? 143 | NSContactsUsageDescription 144 | App需要您的同意,才能使用通讯录,是否同意? 145 | NSHealthShareUsageDescription 146 | App需要您的同意,才能访问健康分享,是否同意? 147 | NSHealthUpdateUsageDescription 148 | App需要您的同意,才能访问健康更新,是否同意? 149 | NSLocationAlwaysUsageDescription 150 | App需要您的同意,才能始终访问位置 151 | NSLocationUsageDescription 152 | App需要您的同意,才能访问位置,是否同意? 153 | NSLocationWhenInUseUsageDescription 154 | App需要您的同意,才能在使用期间访问位置 155 | NSMicrophoneUsageDescription 156 | App需要您的同意,才能使用麦克风,是否同意? 157 | NSMotionUsageDescription 158 | App需要您的同意,才能访问运动与健身,是否同意? 159 | NSPhotoLibraryUsageDescription 160 | App需要您的同意,才能访问相册,是否同意? 161 | NSPhotoLibraryAddUsageDescription 162 | App需要您的同意,才能保存图片到相册,是否同意? 163 | NSRemindersUsageDescription 164 | App需要您的同意,才能访问提醒事项,是否同意? 165 | NSSpeechRecognitionUsageDescription 166 | App需要您的同意,才能使用语音识别,是否同意? 167 | NSFaceIDUsageDescription 168 | App需要您的同意,才能使用Face ID,是否同意? 169 | NSHomeKitUsageDescription 170 | App需要您的同意,才能使用HomeKit,是否同意? 171 | NFCReaderUsageDescription 172 | App需要您的同意,才能使用NFC,是否同意? 173 | NSSiriUsageDescription 174 | App需要您的同意,才能使用Siri,是否同意? 175 | */ 176 | 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [DDYAuthManager](https://github.com/RainOpen/DDYAuthManager) 2 | 3 | ![DDYAuthManager.png](https://github.com/starainDou/DDYDemoImage/blob/master/DDYAuthManager.png) ![DDYAuthManager2.png](https://github.com/starainDou/DDYDemoImage/blob/master/DDYAuthManager2.png) 4 | 5 | * 各种权限验证(主动申请权限)管理,麦克风权限,相机权限,相册,日历,备忘录,联网权限,推送通知权限,定位权限,语音识别权限等等 6 | * 各种权限申请(有的不支持二次申请,比如联网权限,在国行版iOS10+一个bundleID只会询问一次) 7 | * 先鉴定权限,有权限才能进一步执行某些业务 8 | 9 | > # 集成 10 | 11 | * CocoaPods方式 12 | 13 | 1.pod 'DDYAuthManager', '~> 1.4.0' (更多权限需要用1.3.0 如健康数据,speech等) 14 | 15 | 2.#import 16 | 17 | [使用方案](https://github.com/starainDou/DDYAuthorityManager) 18 | 19 | * 文件夹拖入工程方式 20 | 21 | 1.下载工程解压后将'DDYAuthManager'文件夹拖到工程中 22 | 23 | 2.#import "DDYAuthManager.h" 24 | 25 | > # 配置plist 26 | 27 | * 具体信息根据需要自行修改 28 | 29 | ``` 30 | 31 | NSAppleMusicUsageDescription 32 | App需要您的同意,才能访问媒体资料库,是否同意? 33 | NSBluetoothPeripheralUsageDescription 34 | App需要您的同意,才能访问蓝牙,是否同意? 35 | NSCalendarsUsageDescription 36 | App需要您的同意,才能访问日历,是否同意? 37 | NSCameraUsageDescription 38 | App需要您的同意,才能使用相机,是否同意? 39 | NSContactsUsageDescription 40 | App需要您的同意,才能使用通讯录,是否同意? 41 | NSHealthShareUsageDescription 42 | App需要您的同意,才能访问健康分享,是否同意? 43 | NSHealthUpdateUsageDescription 44 | App需要您的同意,才能访问健康更新,是否同意? 45 | NSLocationAlwaysUsageDescription 46 | App需要您的同意,才能始终访问位置 47 | NSLocationUsageDescription 48 | App需要您的同意,才能访问位置,是否同意? 49 | NSLocationWhenInUseUsageDescription 50 | App需要您的同意,才能在使用期间访问位置 51 | NSMicrophoneUsageDescription 52 | App需要您的同意,才能使用麦克风,是否同意? 53 | NSMotionUsageDescription 54 | App需要您的同意,才能访问运动与健身,是否同意? 55 | NSPhotoLibraryUsageDescription 56 | App需要您的同意,才能访问相册,是否同意? 57 | NSPhotoLibraryAddUsageDescription 58 | App需要您的同意,才能保存图片到相册,是否同意? 59 | NSRemindersUsageDescription 60 | App需要您的同意,才能访问提醒事项,是否同意? 61 | NSSpeechRecognitionUsageDescription 62 | App需要您的同意,才能使用语音识别,是否同意? 63 | NSFaceIDUsageDescription 64 | App需要您的同意,才能使用Face ID,是否同意? 65 | NSHomeKitUsageDescription 66 | App需要您的同意,才能使用HomeKit,是否同意? 67 | NFCReaderUsageDescription 68 | App需要您的同意,才能使用NFC,是否同意? 69 | NSSiriUsageDescription 70 | App需要您的同意,才能使用Siri,是否同意? 71 | ``` 72 | 73 | > # 使用 74 | 75 | ### 录音(麦克风)权限 76 | 77 | * 鉴定权限和请求权限统一 78 | 79 | ``` 80 | [DDYAuthManager ddy_AudioAuthAlertShow:YES success:^{ } fail:^(AVAuthorizationStatus authStatus) {}]; 81 | 82 | // 也可以用 [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) { }];请求录音权限 83 | ``` 84 | 85 | 86 | ### 相机(摄像头)权限 87 | 88 | * 鉴定权限和请求权限统一 89 | 90 | 91 | ``` 92 | [DDYAuthManager ddy_CameraAuthAlertShow:YES success:^{ } fail:^(AVAuthorizationStatus authStatus) {}]; 93 | 94 | // 可以先检查摄像头可用性 [DDYAuthManager isCameraAvailable] 95 | ``` 96 | 97 | 98 | ### 图片(相册)权限 99 | 100 | * 鉴定权限和请求权限统一 101 | 102 | ``` 103 | [DDYAuthManager ddy_AlbumAuthAlertShow:YES success:^{} fail:^(PHAuthorizationStatus authStatus) {}]; 104 | ``` 105 | 106 | 107 | ### 通讯录(联系人)权限 108 | 109 | * 鉴定权限和请求权限统一 110 | 111 | ``` 112 | [DDYAuthManager ddy_ContactsAuthAlertShow:YES success:^{} fail:^(DDYContactsAuthStatus authStatus) {}]; 113 | ``` 114 | 115 | 116 | ### 事件(日历)权限 117 | 118 | * 鉴定权限和请求权限统一 119 | 120 | ``` 121 | [DDYAuthManager ddy_EventAuthAlertShow:YES success:^{} fail:^(EKAuthorizationStatus authStatus) {}]; 122 | ``` 123 | 124 | 125 | ### 备忘录权限 126 | 127 | * 鉴定权限和请求权限统一 128 | 129 | ``` 130 | [DDYAuthManager ddy_ReminderAuthAlertShow:YES success:^{} fail:^(EKAuthorizationStatus authStatus) {}]; 131 | ``` 132 | 133 | 134 | ### 通知(推送)权限 135 | 136 | 137 | * 请求权限(注册通知) 138 | 139 | ``` 140 | if (@available(iOS 10.0, *)) { 141 | UNUserNotificationCenter *currentNotificationCenter = [UNUserNotificationCenter currentNotificationCenter]; 142 | UNAuthorizationOptions options = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; 143 | [currentNotificationCenter requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError * _Nullable error) { 144 | dispatch_async(dispatch_get_main_queue(), ^{ 145 | if (!error) [[UIApplication sharedApplication] registerForRemoteNotifications]; // 注册获得device Token 146 | }); 147 | }]; 148 | } else { 149 | UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound; 150 | UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; 151 | [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; 152 | [[UIApplication sharedApplication] registerForRemoteNotifications]; // 注册获得device Token 153 | } 154 | ``` 155 | 156 | * 鉴定权限 157 | 158 | ``` 159 | [DDYAuthManager ddy_PushNotificationAuthAlertShow:YES success:^{} fail:^{}]; 160 | ``` 161 | 162 | 163 | ### 位置(定位)权限 164 | 165 | * 请求权限 166 | 167 | ``` 168 | // CLLocationManager实例必须是全局的变量,否则授权提示弹框可能不会一直显示。 169 | @property (nonatomic, strong) CLLocationManager *locationManager; 170 | 171 | if ([CLLocationManager locationServicesEnabled]) { 172 | _locationManager = [[CLLocationManager alloc] init]; 173 | [_locationManager requestWhenInUseAuthorization]; 174 | } 175 | ``` 176 | 177 | * 鉴定权限 178 | 179 | ``` 180 | // 先判断服务是否可用 [CLLocationManager locationServicesEnabled] 181 | [DDYAuthManager ddy_LocationAuthType:DDYCLLocationTypeInUse alertShow:YES success:^{} fail:^(CLAuthorizationStatus authStatus) {}]; 182 | ``` 183 | 184 | 185 | ### 语音识别(语音转文字)权限 186 | 187 | * 鉴定权限和请求权限统一 188 | 189 | ``` 190 | if (@available(iOS 10.0, *)) { 191 | [DDYAuthManager ddy_SpeechAuthAlertShow:YES success:^{} fail:^(SFSpeechRecognizerAuthorizationStatus authStatus) {}]; 192 | } 193 | ``` 194 | 195 | 196 | ### 联网权限 197 | 198 | * 请求权限 199 | 200 | 201 | ``` 202 | // 可以采用主动请求一次网络的形式触发 203 | [DDYAuthManager ddy_GetNetAuthWithURL:nil]; 204 | // 如果弹窗不出现,请参照网上方案 [0](https://github.com/Zuikyo/ZIKCellularAuthorization) [1](https://www.jianshu.com/p/244c0774b1fb) [2](https://github.com/ziecho/ZYNetworkAccessibity) 205 | ``` 206 | 207 | * 鉴定权限 208 | 209 | ``` 210 | if (@available(iOS 10.0, *)) { 211 | [DDYAuthManager ddy_NetAuthAlertShow:YES success:^{} fail:^(CTCellularDataRestrictedState authStatus) {}]; 212 | } 213 | ``` 214 | 215 | 216 | 217 | 附: 218 | 219 | * 如果pod search DDYAuthManager搜索不到,可以尝试先执行 rm ~/Library/Caches/CocoaPods/search_index.json 220 | * 联网权限不弹窗或者wifi下弹窗拒绝蜂窝网络问题 见https://github.com/Zuikyo/ZIKCellularAuthorization 221 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManagerExample/ViewController.m: -------------------------------------------------------------------------------- 1 | /** MARK: - DDYAuthManager 2018/10/22 2 | * !!!: Author: 豆电雨 3 | * !!!: QQ/WX: 634778311 4 | * !!!: Github: https://github.com/RainOpen/ 5 | * !!!: Blog: https://www.jianshu.com/u/a4bc2516e9e5 6 | * MARK: - ViewController.m 7 | */ 8 | 9 | #import "ViewController.h" 10 | #import "DDYAuthManager.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) UIImage *imgNormal; 15 | 16 | @property (nonatomic, strong) UIImage *imgSelect; 17 | 18 | 19 | @end 20 | 21 | @implementation ViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | _imgNormal = [self circleBorderWithColor:[UIColor grayColor] radius:8]; 26 | _imgSelect = [self circleImageWithColor:[UIColor greenColor] radius:8]; 27 | 28 | [self registerRemoteNotification]; 29 | [self requestNetAuth]; 30 | 31 | NSArray *authArray = @[@"麦克风", @"摄像头", @"相册", @"通讯录", @"推送", @"定位", @"联网权限(iOS10+)"]; 32 | for (NSInteger i = 0; i < authArray.count; i++) { 33 | @autoreleasepool { 34 | UIButton *button = [self generateButton:i title:authArray[i]]; 35 | if ([[NSUserDefaults standardUserDefaults] valueForKey:[NSString stringWithFormat:@"%ld_auth", button.tag]]) { 36 | [self performSelectorOnMainThread:@selector(handleClick:) withObject:button waitUntilDone:YES]; 37 | } 38 | } 39 | } 40 | } 41 | 42 | #pragma mark 联网权限 43 | - (void)requestNetAuth { 44 | [DDYAuthManager ddy_GetNetAuthWithURL:nil]; 45 | } 46 | 47 | #pragma mark 远程推送通知 实际要在Appdelegate(可使用分类) 48 | - (void)registerRemoteNotification { 49 | if (@available(iOS 10.0, *)) { 50 | UNUserNotificationCenter *currentNotificationCenter = [UNUserNotificationCenter currentNotificationCenter]; 51 | UNAuthorizationOptions options = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; 52 | [currentNotificationCenter requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError * _Nullable error) { 53 | dispatch_async(dispatch_get_main_queue(), ^{ 54 | if (!error) [[UIApplication sharedApplication] registerForRemoteNotifications]; // 注册获得device Token 55 | }); 56 | }]; 57 | } else { 58 | UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound; 59 | UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; 60 | [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; 61 | [[UIApplication sharedApplication] registerForRemoteNotifications]; // 注册获得device Token 62 | } 63 | } 64 | 65 | - (UIButton *)generateButton:(NSInteger)tag title:(NSString *)title { 66 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 67 | [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 68 | [button setTitleColor:[UIColor greenColor] forState:UIControlStateSelected]; 69 | [button setImage:_imgNormal forState:UIControlStateNormal]; 70 | [button setImage:_imgSelect forState:UIControlStateSelected]; 71 | [button addTarget:self action:@selector(handleClick:) forControlEvents:UIControlEventTouchUpInside]; 72 | [button setTitle:title forState:UIControlStateNormal]; 73 | [button setTag:tag+100]; 74 | [self.view addSubview:button]; 75 | button.titleLabel.font = [UIFont systemFontOfSize:16]; 76 | [button setFrame:CGRectMake(self.view.bounds.size.width/2.-80, tag*45 + 100, 160, 30)]; 77 | button.layer.borderWidth = 1; 78 | button.layer.borderColor = [UIColor redColor].CGColor; 79 | return button; 80 | } 81 | 82 | - (void)handleClick:(UIButton *)sender { 83 | [[NSUserDefaults standardUserDefaults] setValue:@"1" forKey:[NSString stringWithFormat:@"%ld_auth", sender.tag]]; 84 | if (sender.tag == 100) { 85 | [DDYAuthManager ddy_AudioAuthAlertShow:YES success:^{ 86 | sender.selected = YES; 87 | } fail:^(AVAuthorizationStatus authStatus) { }]; 88 | } else if (sender.tag == 101) { 89 | if ([DDYAuthManager isCameraAvailable]) { 90 | [DDYAuthManager ddy_CameraAuthAlertShow:YES success:^{ 91 | sender.selected = YES; 92 | } fail:^(AVAuthorizationStatus authStatus) { }]; 93 | } else { 94 | sender.selected = NO; 95 | [self showAlertWithMessage:@"摄像头不可用"]; 96 | } 97 | } else if (sender.tag == 102) { 98 | [DDYAuthManager ddy_AlbumAuthAlertShow:YES success:^{ 99 | sender.selected = YES; 100 | } fail:^(PHAuthorizationStatus authStatus) { }]; 101 | } else if (sender.tag == 103) { 102 | [DDYAuthManager ddy_ContactsAuthAlertShow:YES success:^{ 103 | sender.selected = YES; 104 | } fail:^(DDYContactsAuthStatus authStatus) { }]; 105 | } else if (sender.tag == 104) { 106 | [DDYAuthManager ddy_PushNotificationAuthAlertShow:YES success:^{ 107 | sender.selected = YES; 108 | } fail:^{ }]; 109 | } else if (sender.tag == 105) { 110 | if ([CLLocationManager locationServicesEnabled]) { 111 | [DDYAuthManager ddy_LocationAuthType:DDYCLLocationTypeAuthorized alertShow:YES success:^{ 112 | sender.selected = YES; 113 | } fail:^(CLAuthorizationStatus authStatus) { 114 | sender.selected = NO; 115 | NSLog(@"定位服务被拒绝"); 116 | }]; 117 | } else { 118 | sender.selected = NO; 119 | NSLog(@"定位服务未开启"); 120 | } 121 | 122 | } else if (sender.tag == 106) { 123 | if (@available(iOS 10.0, *)) { 124 | [DDYAuthManager ddy_NetAuthAlertShow:YES success:^{ 125 | sender.selected = YES; 126 | } fail:^(CTCellularDataRestrictedState authStatus) { }]; 127 | } else { 128 | sender.selected = YES; 129 | } 130 | } 131 | } 132 | 133 | - (void)showAlertWithMessage:(NSString *)message 134 | { 135 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert]; 136 | [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; 137 | [self presentViewController:alert animated:YES completion:nil]; 138 | } 139 | 140 | #pragma mark 绘制圆形图片 141 | - (UIImage *)circleImageWithColor:(UIColor *)color radius:(CGFloat)radius 142 | { 143 | CGRect rect = CGRectMake(0, 0, radius*2.0, radius*2.0); 144 | UIGraphicsBeginImageContext(rect.size); 145 | CGContextRef context = UIGraphicsGetCurrentContext(); 146 | CGContextSetFillColorWithColor(context,color.CGColor); 147 | CGContextFillEllipseInRect(context, rect); 148 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 149 | UIGraphicsEndImageContext(); 150 | return img; 151 | } 152 | 153 | #pragma mark 绘制圆形框 154 | - (UIImage *)circleBorderWithColor:(UIColor *)color radius:(CGFloat)radius 155 | { 156 | CGRect rect = CGRectMake(0, 0, radius*2.0, radius*2.0); 157 | UIGraphicsBeginImageContext(rect.size); 158 | CGContextRef context = UIGraphicsGetCurrentContext(); 159 | CGContextAddArc(context, radius, radius, radius-1, 0, 2*M_PI, 0); 160 | CGContextSetStrokeColorWithColor(context, color.CGColor); 161 | CGContextSetLineWidth(context, 1); 162 | CGContextStrokePath(context); 163 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 164 | UIGraphicsEndImageContext(); 165 | return img; 166 | } 167 | 168 | - (void)viewWillAppear:(BOOL)animated { 169 | [super viewWillAppear:animated]; 170 | NSLog(@"回到前台"); 171 | } 172 | 173 | @end 174 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A469EA752179DD2300174EAD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A469EA6E2179DD2300174EAD /* AppDelegate.m */; }; 11 | A469EA762179DD2300174EAD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A469EA6F2179DD2300174EAD /* LaunchScreen.storyboard */; }; 12 | A469EA782179DD2300174EAD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A469EA722179DD2300174EAD /* main.m */; }; 13 | A469EA792179DD2300174EAD /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A469EA742179DD2300174EAD /* ViewController.m */; }; 14 | A469EA7C2179DD7600174EAD /* DDYAuthManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A469EA7B2179DD7600174EAD /* DDYAuthManager.m */; }; 15 | A469EA89217DB02800174EAD /* NSBundle+DDYAuthManger.m in Sources */ = {isa = PBXBuildFile; fileRef = A469EA88217DB02800174EAD /* NSBundle+DDYAuthManger.m */; }; 16 | A469EA8B217DB47900174EAD /* DDYAuthManager.bundle in Resources */ = {isa = PBXBuildFile; fileRef = A469EA8A217DB47900174EAD /* DDYAuthManager.bundle */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | A469EA562179DC2E00174EAD /* DDYAuthManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DDYAuthManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | A469EA6D2179DD2300174EAD /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 22 | A469EA6E2179DD2300174EAD /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 23 | A469EA702179DD2300174EAD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 24 | A469EA712179DD2300174EAD /* DDYAuthManagerInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DDYAuthManagerInfo.plist; sourceTree = ""; }; 25 | A469EA722179DD2300174EAD /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 26 | A469EA732179DD2300174EAD /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 27 | A469EA742179DD2300174EAD /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 28 | A469EA7A2179DD7600174EAD /* DDYAuthManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DDYAuthManager.h; sourceTree = ""; }; 29 | A469EA7B2179DD7600174EAD /* DDYAuthManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DDYAuthManager.m; sourceTree = ""; }; 30 | A469EA7F2179E37A00174EAD /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.strings"; sourceTree = ""; }; 31 | A469EA87217DB02800174EAD /* NSBundle+DDYAuthManger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSBundle+DDYAuthManger.h"; sourceTree = ""; }; 32 | A469EA88217DB02800174EAD /* NSBundle+DDYAuthManger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+DDYAuthManger.m"; sourceTree = ""; }; 33 | A469EA8A217DB47900174EAD /* DDYAuthManager.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = DDYAuthManager.bundle; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | A469EA532179DC2E00174EAD /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | A469EA4D2179DC2E00174EAD = { 48 | isa = PBXGroup; 49 | children = ( 50 | A469EA6C2179DD2300174EAD /* DDYAuthManagerExample */, 51 | A469EA582179DC2E00174EAD /* DDYAuthManager */, 52 | A469EA572179DC2E00174EAD /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | A469EA572179DC2E00174EAD /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | A469EA562179DC2E00174EAD /* DDYAuthManager.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | A469EA582179DC2E00174EAD /* DDYAuthManager */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | A469EA7A2179DD7600174EAD /* DDYAuthManager.h */, 68 | A469EA7B2179DD7600174EAD /* DDYAuthManager.m */, 69 | A469EA87217DB02800174EAD /* NSBundle+DDYAuthManger.h */, 70 | A469EA88217DB02800174EAD /* NSBundle+DDYAuthManger.m */, 71 | A469EA8A217DB47900174EAD /* DDYAuthManager.bundle */, 72 | ); 73 | path = DDYAuthManager; 74 | sourceTree = ""; 75 | }; 76 | A469EA6C2179DD2300174EAD /* DDYAuthManagerExample */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | A469EA6D2179DD2300174EAD /* AppDelegate.h */, 80 | A469EA6E2179DD2300174EAD /* AppDelegate.m */, 81 | A469EA6F2179DD2300174EAD /* LaunchScreen.storyboard */, 82 | A469EA712179DD2300174EAD /* DDYAuthManagerInfo.plist */, 83 | A469EA722179DD2300174EAD /* main.m */, 84 | A469EA732179DD2300174EAD /* ViewController.h */, 85 | A469EA742179DD2300174EAD /* ViewController.m */, 86 | ); 87 | path = DDYAuthManagerExample; 88 | sourceTree = ""; 89 | }; 90 | /* End PBXGroup section */ 91 | 92 | /* Begin PBXNativeTarget section */ 93 | A469EA552179DC2E00174EAD /* DDYAuthManager */ = { 94 | isa = PBXNativeTarget; 95 | buildConfigurationList = A469EA692179DC3200174EAD /* Build configuration list for PBXNativeTarget "DDYAuthManager" */; 96 | buildPhases = ( 97 | A469EA522179DC2E00174EAD /* Sources */, 98 | A469EA532179DC2E00174EAD /* Frameworks */, 99 | A469EA542179DC2E00174EAD /* Resources */, 100 | ); 101 | buildRules = ( 102 | ); 103 | dependencies = ( 104 | ); 105 | name = DDYAuthManager; 106 | productName = DDYAuthManager; 107 | productReference = A469EA562179DC2E00174EAD /* DDYAuthManager.app */; 108 | productType = "com.apple.product-type.application"; 109 | }; 110 | /* End PBXNativeTarget section */ 111 | 112 | /* Begin PBXProject section */ 113 | A469EA4E2179DC2E00174EAD /* Project object */ = { 114 | isa = PBXProject; 115 | attributes = { 116 | LastUpgradeCheck = 1100; 117 | ORGANIZATIONNAME = com.smartmesh; 118 | TargetAttributes = { 119 | A469EA552179DC2E00174EAD = { 120 | CreatedOnToolsVersion = 9.3; 121 | }; 122 | }; 123 | }; 124 | buildConfigurationList = A469EA512179DC2E00174EAD /* Build configuration list for PBXProject "DDYAuthManager" */; 125 | compatibilityVersion = "Xcode 9.3"; 126 | developmentRegion = en; 127 | hasScannedForEncodings = 0; 128 | knownRegions = ( 129 | en, 130 | Base, 131 | "zh-Hans", 132 | ); 133 | mainGroup = A469EA4D2179DC2E00174EAD; 134 | productRefGroup = A469EA572179DC2E00174EAD /* Products */; 135 | projectDirPath = ""; 136 | projectRoot = ""; 137 | targets = ( 138 | A469EA552179DC2E00174EAD /* DDYAuthManager */, 139 | ); 140 | }; 141 | /* End PBXProject section */ 142 | 143 | /* Begin PBXResourcesBuildPhase section */ 144 | A469EA542179DC2E00174EAD /* Resources */ = { 145 | isa = PBXResourcesBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | A469EA762179DD2300174EAD /* LaunchScreen.storyboard in Resources */, 149 | A469EA8B217DB47900174EAD /* DDYAuthManager.bundle in Resources */, 150 | ); 151 | runOnlyForDeploymentPostprocessing = 0; 152 | }; 153 | /* End PBXResourcesBuildPhase section */ 154 | 155 | /* Begin PBXSourcesBuildPhase section */ 156 | A469EA522179DC2E00174EAD /* Sources */ = { 157 | isa = PBXSourcesBuildPhase; 158 | buildActionMask = 2147483647; 159 | files = ( 160 | A469EA792179DD2300174EAD /* ViewController.m in Sources */, 161 | A469EA89217DB02800174EAD /* NSBundle+DDYAuthManger.m in Sources */, 162 | A469EA7C2179DD7600174EAD /* DDYAuthManager.m in Sources */, 163 | A469EA782179DD2300174EAD /* main.m in Sources */, 164 | A469EA752179DD2300174EAD /* AppDelegate.m in Sources */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | /* End PBXSourcesBuildPhase section */ 169 | 170 | /* Begin PBXVariantGroup section */ 171 | A469EA6F2179DD2300174EAD /* LaunchScreen.storyboard */ = { 172 | isa = PBXVariantGroup; 173 | children = ( 174 | A469EA702179DD2300174EAD /* Base */, 175 | A469EA7F2179E37A00174EAD /* zh-Hans */, 176 | ); 177 | name = LaunchScreen.storyboard; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXVariantGroup section */ 181 | 182 | /* Begin XCBuildConfiguration section */ 183 | A469EA672179DC3200174EAD /* Debug */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | ALWAYS_SEARCH_USER_PATHS = NO; 187 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 188 | CLANG_ANALYZER_NONNULL = YES; 189 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 190 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 191 | CLANG_CXX_LIBRARY = "libc++"; 192 | CLANG_ENABLE_MODULES = YES; 193 | CLANG_ENABLE_OBJC_ARC = YES; 194 | CLANG_ENABLE_OBJC_WEAK = YES; 195 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 196 | CLANG_WARN_BOOL_CONVERSION = YES; 197 | CLANG_WARN_COMMA = YES; 198 | CLANG_WARN_CONSTANT_CONVERSION = YES; 199 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 201 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 202 | CLANG_WARN_EMPTY_BODY = YES; 203 | CLANG_WARN_ENUM_CONVERSION = YES; 204 | CLANG_WARN_INFINITE_RECURSION = YES; 205 | CLANG_WARN_INT_CONVERSION = YES; 206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 208 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 211 | CLANG_WARN_STRICT_PROTOTYPES = YES; 212 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 213 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 214 | CLANG_WARN_UNREACHABLE_CODE = YES; 215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 216 | CODE_SIGN_IDENTITY = "iPhone Developer"; 217 | COPY_PHASE_STRIP = NO; 218 | DEBUG_INFORMATION_FORMAT = dwarf; 219 | ENABLE_STRICT_OBJC_MSGSEND = YES; 220 | ENABLE_TESTABILITY = YES; 221 | GCC_C_LANGUAGE_STANDARD = gnu11; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_NO_COMMON_BLOCKS = YES; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "DEBUG=1", 227 | "$(inherited)", 228 | ); 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 236 | MTL_ENABLE_DEBUG_INFO = YES; 237 | ONLY_ACTIVE_ARCH = YES; 238 | SDKROOT = iphoneos; 239 | }; 240 | name = Debug; 241 | }; 242 | A469EA682179DC3200174EAD /* Release */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 250 | CLANG_CXX_LIBRARY = "libc++"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_ENABLE_OBJC_WEAK = YES; 254 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 255 | CLANG_WARN_BOOL_CONVERSION = YES; 256 | CLANG_WARN_COMMA = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 259 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 260 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 261 | CLANG_WARN_EMPTY_BODY = YES; 262 | CLANG_WARN_ENUM_CONVERSION = YES; 263 | CLANG_WARN_INFINITE_RECURSION = YES; 264 | CLANG_WARN_INT_CONVERSION = YES; 265 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 266 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 269 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 270 | CLANG_WARN_STRICT_PROTOTYPES = YES; 271 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 272 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | CODE_SIGN_IDENTITY = "iPhone Developer"; 276 | COPY_PHASE_STRIP = NO; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | ENABLE_NS_ASSERTIONS = NO; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | GCC_C_LANGUAGE_STANDARD = gnu11; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 289 | MTL_ENABLE_DEBUG_INFO = NO; 290 | SDKROOT = iphoneos; 291 | VALIDATE_PRODUCT = YES; 292 | }; 293 | name = Release; 294 | }; 295 | A469EA6A2179DC3200174EAD /* Debug */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | CODE_SIGN_STYLE = Automatic; 299 | DEVELOPMENT_TEAM = X634X4Y33S; 300 | INFOPLIST_FILE = "$(SRCROOT)/DDYAuthManagerExample/DDYAuthManagerInfo.plist"; 301 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 302 | LD_RUNPATH_SEARCH_PATHS = ( 303 | "$(inherited)", 304 | "@executable_path/Frameworks", 305 | ); 306 | MARKETING_VERSION = 1.2.0; 307 | PRODUCT_BUNDLE_IDENTIFIER = com.dengyun.dengyunblln; 308 | PRODUCT_NAME = "$(TARGET_NAME)"; 309 | TARGETED_DEVICE_FAMILY = "1,2"; 310 | }; 311 | name = Debug; 312 | }; 313 | A469EA6B2179DC3200174EAD /* Release */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | CODE_SIGN_STYLE = Automatic; 317 | DEVELOPMENT_TEAM = X634X4Y33S; 318 | INFOPLIST_FILE = "$(SRCROOT)/DDYAuthManagerExample/DDYAuthManagerInfo.plist"; 319 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 320 | LD_RUNPATH_SEARCH_PATHS = ( 321 | "$(inherited)", 322 | "@executable_path/Frameworks", 323 | ); 324 | MARKETING_VERSION = 1.2.0; 325 | PRODUCT_BUNDLE_IDENTIFIER = com.dengyun.dengyunblln; 326 | PRODUCT_NAME = "$(TARGET_NAME)"; 327 | TARGETED_DEVICE_FAMILY = "1,2"; 328 | }; 329 | name = Release; 330 | }; 331 | /* End XCBuildConfiguration section */ 332 | 333 | /* Begin XCConfigurationList section */ 334 | A469EA512179DC2E00174EAD /* Build configuration list for PBXProject "DDYAuthManager" */ = { 335 | isa = XCConfigurationList; 336 | buildConfigurations = ( 337 | A469EA672179DC3200174EAD /* Debug */, 338 | A469EA682179DC3200174EAD /* Release */, 339 | ); 340 | defaultConfigurationIsVisible = 0; 341 | defaultConfigurationName = Release; 342 | }; 343 | A469EA692179DC3200174EAD /* Build configuration list for PBXNativeTarget "DDYAuthManager" */ = { 344 | isa = XCConfigurationList; 345 | buildConfigurations = ( 346 | A469EA6A2179DC3200174EAD /* Debug */, 347 | A469EA6B2179DC3200174EAD /* Release */, 348 | ); 349 | defaultConfigurationIsVisible = 0; 350 | defaultConfigurationName = Release; 351 | }; 352 | /* End XCConfigurationList section */ 353 | }; 354 | rootObject = A469EA4E2179DC2E00174EAD /* Project object */; 355 | } 356 | -------------------------------------------------------------------------------- /DDYAuthManager/DDYAuthManager/DDYAuthManager.m: -------------------------------------------------------------------------------- 1 | /// MARK: - DDYAuthManager 2018/10/22 2 | /// !!!: Author: 豆电雨 3 | /// !!!: QQ/WX: 634778311 4 | /// !!!: Github: https://github.com/RainOpen/ 5 | /// !!!: Blog: https://juejin.im/user/57dddcd8128fe10064cadee9 6 | /// MARK: - DDYAuthManager.m 7 | 8 | #import "DDYAuthManager.h" 9 | #import "NSBundle+DDYAuthManger.h" 10 | 11 | @interface DDYAuthManager () 12 | 13 | /// CLLocationManager实例必须是全局的变量,否则授权提示弹框可能不会一直显示。 14 | @property (nonatomic, strong) CLLocationManager *locationManager; 15 | @property (nonatomic, assign) DDYCLLocationType locationType; 16 | @property (nonatomic, assign) BOOL locationShow; 17 | @property (nonatomic, copy) void (^locationSuccessBlock)(void); 18 | @property (nonatomic, copy) void (^locationFailureBlock)(CLAuthorizationStatus); 19 | 20 | @end 21 | 22 | @implementation DDYAuthManager 23 | 24 | + (instancetype)shareInstance { 25 | static DDYAuthManager *shareInstance = nil; 26 | static dispatch_once_t onceToken; 27 | dispatch_once(&onceToken, ^{ 28 | shareInstance = [[self alloc] init]; 29 | }); 30 | return shareInstance; 31 | } 32 | 33 | // [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) { }]; 34 | // MARK: - 麦克风权限 35 | + (void)ddy_AudioAuthAlertShow:(BOOL)show success:(void (^)(void))success fail:(void (^)(AVAuthorizationStatus))fail { 36 | void (^handleResult)(BOOL, AVAuthorizationStatus) = ^(BOOL isAuthorized, AVAuthorizationStatus authStatus) { 37 | if (isAuthorized && success) { 38 | success(); 39 | }; 40 | if (!isAuthorized && show) { 41 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthMicrophone"]]; 42 | } 43 | if (!isAuthorized && fail) { 44 | fail(authStatus); 45 | } 46 | }; 47 | 48 | AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; 49 | if (authStatus == AVAuthorizationStatusNotDetermined) { 50 | [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) { 51 | dispatch_async(dispatch_get_main_queue(), ^{ 52 | handleResult(granted, granted ? AVAuthorizationStatusAuthorized : AVAuthorizationStatusDenied); 53 | }); 54 | }]; 55 | } else { 56 | handleResult(authStatus == AVAuthorizationStatusAuthorized, authStatus); 57 | } 58 | } 59 | 60 | // MARK: - 摄像头(相机)权限 61 | + (void)ddy_CameraAuthAlertShow:(BOOL)show success:(void (^)(void))success fail:(void (^)(AVAuthorizationStatus))fail { 62 | void (^handleResult)(BOOL, AVAuthorizationStatus) = ^(BOOL isAuthorized, AVAuthorizationStatus authStatus) { 63 | if (isAuthorized && success) { 64 | success(); 65 | } 66 | if (!isAuthorized && show) { 67 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthCamera"]]; 68 | } 69 | if (!isAuthorized && fail) { 70 | fail(authStatus); 71 | } 72 | }; 73 | 74 | AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; 75 | if (authStatus == AVAuthorizationStatusNotDetermined) { 76 | [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { 77 | dispatch_async(dispatch_get_main_queue(), ^{ 78 | handleResult(granted, granted ? AVAuthorizationStatusAuthorized : AVAuthorizationStatusDenied); 79 | }); 80 | }]; 81 | } else { 82 | handleResult(authStatus == AVAuthorizationStatusAuthorized, authStatus); 83 | } 84 | } 85 | 86 | // MARK: 判断设备摄像头是否可用 87 | + (BOOL)isCameraAvailable { 88 | return [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; 89 | } 90 | 91 | // MARK: 前面的摄像头是否可用 92 | + (BOOL)isFrontCameraAvailable { 93 | return [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]; 94 | } 95 | 96 | // MARK: 后面的摄像头是否可用 97 | + (BOOL)isRearCameraAvailable { 98 | return [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]; 99 | } 100 | 101 | // MARK: - 相册使用权限(iOS 8+) 102 | + (void)ddy_AlbumAuthAlertShow:(BOOL)show success:(void (^)(void))success fail:(void (^)(PHAuthorizationStatus))fail { 103 | void (^handleResult)(BOOL, PHAuthorizationStatus) = ^(BOOL isAuthorized, PHAuthorizationStatus authStatus) { 104 | if (isAuthorized && success) { 105 | success(); 106 | } 107 | if (!isAuthorized && show) { 108 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthAlbum"]]; 109 | } 110 | if (!isAuthorized && fail) { 111 | fail(authStatus); 112 | } 113 | }; 114 | 115 | PHAuthorizationStatus authStatus = [PHPhotoLibrary authorizationStatus]; 116 | if (authStatus == PHAuthorizationStatusNotDetermined) { 117 | [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { 118 | dispatch_async(dispatch_get_main_queue(), ^{ 119 | handleResult(status == PHAuthorizationStatusAuthorized, status); 120 | }); 121 | }]; 122 | } else { 123 | handleResult(authStatus == PHAuthorizationStatusAuthorized, authStatus); 124 | } 125 | } 126 | 127 | // MARK: - 通讯录权限 128 | + (void)ddy_ContactsAuthAlertShow:(BOOL)show success:(void (^)(void))success fail:(void (^)(DDYContactsAuthStatus))fail { 129 | void (^handleResult)(BOOL, DDYContactsAuthStatus) = ^(BOOL isAuthorized, DDYContactsAuthStatus authStatus) { 130 | if (isAuthorized && success) { 131 | success(); 132 | } 133 | if (!isAuthorized && show) { 134 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthContacts"]]; 135 | } 136 | if (!isAuthorized && fail) { 137 | fail(authStatus); 138 | } 139 | }; 140 | 141 | if (@available(iOS 9.0, *)) { 142 | CNAuthorizationStatus authStatus = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]; 143 | if (authStatus == CNAuthorizationStatusNotDetermined) { 144 | CNContactStore *contactStore = [[CNContactStore alloc] init]; 145 | [contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) { 146 | dispatch_async(dispatch_get_main_queue(), ^{ 147 | handleResult(granted, granted ? DDYContactsAuthStatusAuthorized : DDYContactsAuthStatusDenied); 148 | }); 149 | }]; 150 | } else if (authStatus == CNAuthorizationStatusRestricted) { 151 | handleResult(NO, DDYContactsAuthStatusRestricted); 152 | } else if (authStatus == CNAuthorizationStatusDenied) { 153 | handleResult(NO, DDYContactsAuthStatusDenied); 154 | } else if (authStatus == CNAuthorizationStatusAuthorized) { 155 | handleResult(YES, DDYContactsAuthStatusAuthorized); 156 | } 157 | } else { 158 | ABAuthorizationStatus authStatus = ABAddressBookGetAuthorizationStatus(); 159 | if (authStatus == kABAuthorizationStatusNotDetermined) { 160 | ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL); 161 | ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { 162 | dispatch_async(dispatch_get_main_queue(), ^{ 163 | handleResult(granted, granted ? DDYContactsAuthStatusAuthorized : DDYContactsAuthStatusDenied); 164 | }); 165 | }); 166 | } else if (authStatus == kABAuthorizationStatusRestricted) { 167 | handleResult(NO, DDYContactsAuthStatusRestricted); 168 | } else if (authStatus == kABAuthorizationStatusDenied) { 169 | handleResult(NO, DDYContactsAuthStatusDenied); 170 | } else if (authStatus == kABAuthorizationStatusAuthorized) { 171 | handleResult(YES, DDYContactsAuthStatusAuthorized); 172 | } 173 | } 174 | } 175 | 176 | /** 关于APP首次安装(一旦用户选择后即使卸载重装也不会再弹窗)联网权限弹窗,无法得知用户是否点击允许或不允许(苹果没有给出api),所以常规按以下流程自行判断 177 | * 1.用keychain记录是否是首次安装(和弹窗一致),若是首次安装则启动APP后进入特定界面(如引导页),如果不是那么严格忽略该步骤。 178 | * 2.用 -checkNetworkConnectWhenAirplaneModeOrNoWlanCellular 判断是否特殊情况(完全无网络时首次启动APP,不会联网权限弹窗)。 179 | * 3.若第二步通过(如果不考虑极端情况直接该步骤),用户有以太网进入则发送网络请求(最好head请求,省流量且更快速),如果是首次联网则可能弹窗。 180 | 不确定是否弹窗(2G网络,弱网,飞行模式或同时无wifi和蜂窝网络,只wifi但wifi并没有以太网等等情况不弹窗),也不确定如果弹窗用户的选择。 181 | * 4.进入真正页面,用Reachability(或AFNetworkReachabilityStatusNotReachable或RealReachability)判断网络状态。 182 | * 5.用 -fetchSSIDInfo 或 -fetchMobileInfo 判断确实存在能使得弹窗的网络。 183 | * 6.用CTCellularData获取状态,此时粗略得到用户是否授权联网权限。 184 | */ 185 | // MARK: - 用网络请求方式主动获取一次权限 186 | + (void)ddy_GetNetAuthWithURL:(NSURL *)url { 187 | // 为了快速请求且流量最小化,这里用Head请求,只获取响应头 188 | NSURLSession *session = [NSURLSession sharedSession]; 189 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:(url ? url : [NSURL URLWithString:@"https://www.baidu.com"])]; 190 | [request setHTTPMethod:@"HEAD"]; 191 | NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) { 192 | // 拿到响应头信息 193 | NSHTTPURLResponse *res = (NSHTTPURLResponse *)response; 194 | // 解析拿到的响应数据 195 | NSLog(@"DDYAuthManager request baidu:\n%s_%@\n%@",__FUNCTION__, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], res.allHeaderFields); 196 | }]; 197 | [dataTask resume]; 198 | } 199 | 200 | // MARK: - 联网权限 iOS 10+ 201 | /** 202 | // 网络权限更改回调,如果不想每次改变都回调那记得置nil 203 | cellularData.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState state) { 204 | dispatch_async(dispatch_get_main_queue(), ^{ 205 | if (state == kCTCellularDataNotRestricted) { 206 | if (authState != state) handleResult(YES, state); 207 | } else { 208 | if (authState != state) handleResult(NO, state); 209 | if (show && authState != state) handleResult(NO, authState); 210 | } 211 | }); 212 | }; 213 | */ 214 | + (void)ddy_NetAuthAlertShow:(BOOL)show success:(void (^)(void))success fail:(void (^)(CTCellularDataRestrictedState))fail { 215 | // CTCellularData在iOS9之前是私有类,但联网权限设置是iOS10开始的 216 | if (@available(iOS 10.0, *)) { 217 | void (^handleResult)(BOOL, CTCellularDataRestrictedState) = ^(BOOL isAuthorized, CTCellularDataRestrictedState authStatus) { 218 | if (isAuthorized && success) { 219 | success(); 220 | } 221 | if (!isAuthorized && show) { 222 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthNetwork"]]; 223 | } 224 | if (!isAuthorized && fail) { 225 | fail(authStatus); 226 | } 227 | }; 228 | CTCellularData *cellularData = [[CTCellularData alloc] init]; 229 | CTCellularDataRestrictedState authState = cellularData.restrictedState; 230 | if (authState == kCTCellularDataNotRestricted) { 231 | handleResult(YES, authState); 232 | } else if (authState == kCTCellularDataRestricted) { 233 | handleResult(NO, authState); 234 | } else { 235 | // CTCellularData刚实例化对象时可能kCTCellularDataRestrictedStateUnknown,所以延迟一下 236 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 237 | CTCellularDataRestrictedState authState2 = cellularData.restrictedState; 238 | if (authState2 == kCTCellularDataNotRestricted) { 239 | handleResult(YES, authState2); 240 | } else if (authState == kCTCellularDataRestricted) { 241 | handleResult(NO, authState2); 242 | } else { 243 | handleResult(NO, authState2); 244 | } 245 | }); 246 | } 247 | } 248 | } 249 | 250 | // MARK: - 推送通知权限 需要在打开 target -> Capabilities —> Push Notifications 251 | + (void)ddy_PushNotificationAuthAlertShow:(BOOL)show success:(void (^)(void))success fail:(void (^)(void))fail { 252 | if (@available(iOS 10.0, *)) { 253 | UNUserNotificationCenter *notiCenter = [UNUserNotificationCenter currentNotificationCenter]; 254 | [notiCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { 255 | if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) { 256 | [notiCenter requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) { 257 | dispatch_async(dispatch_get_main_queue(), ^{ 258 | if (granted && success) { 259 | success(); 260 | } 261 | if (!granted && show) { 262 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthNotifications"]]; 263 | } 264 | if (!granted && fail) { 265 | fail(); 266 | } 267 | }); 268 | }]; 269 | } else { 270 | dispatch_async(dispatch_get_main_queue(), ^{ 271 | if (settings.authorizationStatus == UNAuthorizationStatusAuthorized && success) { 272 | success(); 273 | } 274 | if (settings.authorizationStatus != UNAuthorizationStatusAuthorized && fail) { 275 | fail(); 276 | } 277 | if (show && settings.authorizationStatus == UNAuthorizationStatusDenied) { 278 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthNotifications"]]; 279 | } 280 | }); 281 | } 282 | }]; 283 | } else { 284 | UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings]; 285 | // UIUserNotificationTypeNone 收到通知不呈现UI,可能无权限也可能还未询问权限 286 | if (settings.types != UIUserNotificationTypeNone && success) success(); 287 | if (settings.types == UIUserNotificationTypeNone && fail) fail(); 288 | if (show && settings.types == UIUserNotificationTypeNone) [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthNotifications"]]; 289 | } 290 | } 291 | 292 | // MARK: - 定位权限 293 | + (void)ddy_LocationAuthType:(DDYCLLocationType)type alertShow:(BOOL)show success:(void (^)(void))success fail:(void (^)(CLAuthorizationStatus))fail{ 294 | 295 | CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus]; 296 | 297 | if ([CLLocationManager locationServicesEnabled]) { 298 | if (authStatus == kCLAuthorizationStatusNotDetermined || authStatus == kCLAuthorizationStatusRestricted) { 299 | [DDYAuthManager shareInstance].locationManager = [[CLLocationManager alloc] init]; 300 | [DDYAuthManager shareInstance].locationManager.delegate = [DDYAuthManager shareInstance]; 301 | [[DDYAuthManager shareInstance].locationManager requestAlwaysAuthorization]; 302 | [[DDYAuthManager shareInstance].locationManager requestWhenInUseAuthorization]; 303 | [[DDYAuthManager shareInstance].locationManager startUpdatingLocation]; 304 | [DDYAuthManager shareInstance].locationType = type; 305 | [DDYAuthManager shareInstance].locationShow = show; 306 | [DDYAuthManager shareInstance].locationSuccessBlock = success; 307 | [DDYAuthManager shareInstance].locationFailureBlock = fail; 308 | } else if (authStatus == kCLAuthorizationStatusDenied) { 309 | if (show) { 310 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthLocation"]]; 311 | } 312 | if (fail) { 313 | fail(authStatus); 314 | } 315 | } else { 316 | if (authStatus == kCLAuthorizationStatusAuthorizedAlways && type == DDYCLLocationTypeAlways) { 317 | if (success) { 318 | success(); 319 | } 320 | } else if (authStatus == kCLAuthorizationStatusAuthorizedWhenInUse && type == DDYCLLocationTypeInUse) { 321 | if (success) { 322 | success(); 323 | } 324 | } else if (type == DDYCLLocationTypeAuthorized) { 325 | if (success) { 326 | success(); 327 | } 328 | } else { 329 | if (show) { 330 | [self showAlertWithAuthInfo:[self i18n:@"DDYNoAuthLocation"]]; 331 | } 332 | if (fail) { 333 | fail(authStatus); 334 | } 335 | } 336 | } 337 | } else { 338 | NSLog(@"Location Services 未开启 %d", authStatus); 339 | } 340 | } 341 | 342 | - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { 343 | static dispatch_once_t onceToken; 344 | dispatch_once(&onceToken, ^{ 345 | [DDYAuthManager ddy_LocationAuthType:[DDYAuthManager shareInstance].locationType 346 | alertShow:[DDYAuthManager shareInstance].locationShow 347 | success:[DDYAuthManager shareInstance].locationSuccessBlock 348 | fail:[DDYAuthManager shareInstance].locationFailureBlock]; 349 | }); 350 | } 351 | 352 | - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { 353 | static dispatch_once_t onceToken; 354 | dispatch_once(&onceToken, ^{ 355 | [DDYAuthManager ddy_LocationAuthType:[DDYAuthManager shareInstance].locationType 356 | alertShow:[DDYAuthManager shareInstance].locationShow 357 | success:[DDYAuthManager shareInstance].locationSuccessBlock 358 | fail:[DDYAuthManager shareInstance].locationFailureBlock]; 359 | }); 360 | } 361 | 362 | // MARK: - 私有方法 363 | // MARK: 默认无权限提示 364 | + (void)showAlertWithAuthInfo:(NSString *)authInfo { 365 | NSString *message = [authInfo stringByReplacingOccurrencesOfString:@"%@" withString:[self getAPPName]]; 366 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert]; 367 | [alert addAction:[UIAlertAction actionWithTitle:[self i18n:@"DDYNoAuthCancel"] style:UIAlertActionStyleCancel handler:nil]]; 368 | [alert addAction:[UIAlertAction actionWithTitle:[self i18n:@"DDYNoAuthConfirm"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 369 | if (@available(iOS 10.0, *)) { 370 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil]; 371 | } else { 372 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; 373 | } 374 | }]]; 375 | [[UIApplication sharedApplication].delegate.window.rootViewController presentViewController:alert animated:YES completion:nil]; 376 | } 377 | 378 | // MARK: 获取App名 379 | + (NSString *)getAPPName { 380 | if ([DDYAuthManager shareInstance].appName) { 381 | return [DDYAuthManager shareInstance].appName; 382 | } else { 383 | NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; 384 | // CFShow((__bridge CFTypeRef)(infoDictionary)); 385 | NSString *bundleDisplayName = [infoDictionary objectForKey:@"CFBundleDisplayName"]; 386 | NSString *bundleName = [infoDictionary objectForKey:@"CFBundleName"]; 387 | return bundleDisplayName ?: (bundleName ?: @"App"); 388 | } 389 | } 390 | 391 | // MARK: 国际化支持 392 | + (NSString *)i18n:(NSString *)str { 393 | return [NSBundle ddyAuthManagerLocalizedStringForKey:str]; 394 | } 395 | 396 | @end 397 | --------------------------------------------------------------------------------